Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy-playground-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
test:
needs: deploy
timeout-minutes: 60
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:
- name: Checkout code
Expand Down
2 changes: 2 additions & 0 deletions packages/tests-e2e/playwright.config.connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default defineConfig({
timeout: operationTimeout, // default: 5000ms
},
use: {
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 15.3.2) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/129.0.6668.29 Safari/537.36',
actionTimeout: operationTimeout, // default: none
navigationTimeout: operationTimeout, // default: none
baseURL: process.env.PLAYWRIGHT_TEST_URL,
Expand Down
15 changes: 13 additions & 2 deletions packages/tests-e2e/src/connect/models/AppendModel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Page } from '@playwright/test';

import { ErrorTexts } from '../utils/Constants';
import { expectError } from '../utils/ExpectScreen';
import { ErrorTexts, ScreenNames } from '../utils/Constants';
import { expectError, expectScreen } from '../utils/ExpectScreen';
import type { VirtualAuthenticator } from '../utils/VirtualAuthenticator';

export class AppendModel {
Expand All @@ -24,6 +24,17 @@ export class AppendModel {
}
}

autoAppendPasskey(complete: boolean, operationTrigger: () => Promise<void>) {
if (complete) {
return this.authenticator.startAndCompletePasskeyOperation(operationTrigger);
} else {
return this.authenticator.startAndCancelPasskeyOperation(operationTrigger, async () => {
await expectScreen(this.page, ScreenNames.PasskeyAppend);
await this.page.waitForSelector('.button-loading-container', { state: 'detached' });
});
}
}

confirmAppended() {
return this.page.getByRole('button', { name: 'Continue' }).click();
}
Expand Down
9 changes: 4 additions & 5 deletions packages/tests-e2e/src/connect/models/BaseModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ export class BaseModel {
this.page = page;
this.authenticator = authenticator;
this.blocker = blocker;
this.signup = new SignupModel(page);
this.login = new LoginModel(page, authenticator);
this.append = new AppendModel(page, authenticator);
this.signup = new SignupModel(page, this.append);
this.mfa = new MFAModel(page, this.append);
this.home = new HomeModel(page);
this.passkeyList = new PasskeyListModel(page, authenticator);
this.webhook = new WebhookModel(page);
this.storage = new StorageModel(page);
this.mfa = new MFAModel(page);
}

loadSignup() {
Expand All @@ -64,15 +64,14 @@ export class BaseModel {

async createUser(invited: boolean, append: boolean) {
this.email = await this.signup.autofillCredentials();
await this.signup.submit();
await this.signup.submit(invited, append);
this.mfa.registerTokenUsed();
if (invited) {
await this.expectScreen(ScreenNames.PasskeyAppend);
if (append) {
await this.append.appendPasskey(true);
await this.expectScreen(ScreenNames.PasskeyAppended);
await this.append.confirmAppended();
} else {
await this.expectScreen(ScreenNames.PasskeyAppend);
await this.append.skipAppend();
}
}
Expand Down
15 changes: 12 additions & 3 deletions packages/tests-e2e/src/connect/models/MFAModel.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';

import type { AppendModel } from './AppendModel';

export class MFAModel {
page: Page;
append: AppendModel;
timestamp: number;

constructor(page: Page) {
constructor(page: Page, append: AppendModel) {
this.page = page;
this.append = append;
this.timestamp = Date.now();
}

Expand All @@ -21,7 +25,12 @@ export class MFAModel {
this.registerTokenUsed();
}

submit() {
return this.page.getByRole('button', { name: 'Submit' }).click();
submit(invited: boolean, autoAppend: boolean) {
if (invited) {
const operationTrigger = () => this.page.getByRole('button', { name: 'Submit' }).click();
return this.append.autoAppendPasskey(autoAppend, operationTrigger);
} else {
return this.page.getByRole('button', { name: 'Submit' }).click();
}
}
}
15 changes: 12 additions & 3 deletions packages/tests-e2e/src/connect/models/SignupModel.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import type { Page } from '@playwright/test';

import type { AppendModel } from './AppendModel';

export class SignupModel {
page: Page;
append: AppendModel;

constructor(page: Page) {
constructor(page: Page, append: AppendModel) {
this.page = page;
this.append = append;
}

async autofillCredentials(): Promise<string> {
await this.page.getByRole('button', { name: 'auto' }).click();
return await this.page.getByPlaceholder('Email').inputValue();
}

submit() {
return this.page.getByRole('button', { name: 'Sign up' }).click();
submit(invited: boolean, autoAppend: boolean) {
if (invited) {
const operationTrigger = () => this.page.getByRole('button', { name: 'Sign up' }).click();
return this.append.autoAppendPasskey(autoAppend, operationTrigger);
} else {
return this.page.getByRole('button', { name: 'Sign up' }).click();
}
}
}
14 changes: 7 additions & 7 deletions packages/tests-e2e/src/connect/scenarios/append.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ import { expect } from '@playwright/test';

import { test } from '../fixtures/BaseTest';
import { password, ScreenNames } from '../utils/Constants';
import { loadPasskeyAppend, setupNetworkBlocker, setupUser, setupVirtualAuthenticator } from './hooks';
import { loadBeforePasskeyAppend, setupNetworkBlocker, setupUser, setupVirtualAuthenticator } from './hooks';

test.describe('append component', () => {
setupVirtualAuthenticator(test);
setupNetworkBlocker(test);
setupUser(test, true, false);
loadPasskeyAppend(test);
loadBeforePasskeyAppend(test);

test('successful passkey append on login', async ({ model }) => {
await model.append.appendPasskey(true);
await model.mfa.submit(true, true);
await model.expectScreen(ScreenNames.PasskeyAppended);

await model.append.confirmAppended();
await model.expectScreen(ScreenNames.Home);
});

test('failed passkey append on login', async ({ model }) => {
await model.append.appendPasskey(false);
await model.mfa.submit(true, false);
});

test('Corbado FAPI unavailable after authentication', async ({ model }) => {
await model.blocker.blockCorbadoFAPIFinishEndpoint();

await model.append.appendPasskey(true);
await model.mfa.submit(true, true);
await model.expectScreen(ScreenNames.Home);
});
});
Expand All @@ -48,7 +48,7 @@ test.describe('skip append component', () => {
await model.expectScreen(ScreenNames.MFA);

await model.mfa.autofillTOTP();
await model.mfa.submit();
await model.mfa.submit(false, false);

await model.expectScreen(ScreenNames.Home);
});
Expand All @@ -67,7 +67,7 @@ test.describe('skip append component', () => {
await model.expectScreen(ScreenNames.MFA);

await model.mfa.autofillTOTP();
await model.mfa.submit();
await model.mfa.submit(false, false);
await model.expectScreen(ScreenNames.Home);
});
});
6 changes: 3 additions & 3 deletions packages/tests-e2e/src/connect/scenarios/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function setupUser(
}

// assumes that setupUser(test, true, false) has been called right before
export function loadPasskeyAppend(
export function loadBeforePasskeyAppend(
test: TestType<
PlaywrightTestArgs & PlaywrightTestOptions & { model: BaseModel },
PlaywrightWorkerArgs & PlaywrightWorkerOptions
Expand All @@ -100,8 +100,8 @@ export function loadPasskeyAppend(
await model.expectScreen(ScreenNames.MFA);

await model.mfa.autofillTOTP();
await model.mfa.submit();
await model.expectScreen(ScreenNames.PasskeyAppend);
// await model.mfa.submit();
// await model.expectScreen(ScreenNames.PasskeyAppend);
});
}

Expand Down
5 changes: 2 additions & 3 deletions packages/tests-e2e/src/connect/scenarios/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test.describe('login component (without invitation token)', () => {
await model.expectScreen(ScreenNames.MFA);

await model.mfa.autofillTOTP();
await model.mfa.submit();
await model.mfa.submit(false, false);
await model.expectScreen(ScreenNames.Home);
});
});
Expand All @@ -35,8 +35,7 @@ test.describe('login component (with invitation token, without passkeys)', () =>
await model.expectScreen(ScreenNames.MFA);

await model.mfa.autofillTOTP();
await model.mfa.submit();
await model.expectScreen(ScreenNames.PasskeyAppend);
await model.mfa.submit(true, false);

await model.append.skipAppend();
await model.expectScreen(ScreenNames.Home);
Expand Down
6 changes: 3 additions & 3 deletions packages/tests-e2e/src/connect/scenarios/misc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test } from '../fixtures/BaseTest';
import { ScreenNames, WebhookTypes } from '../utils/Constants';
import {
loadPasskeyAppend,
loadBeforePasskeyAppend,
loadPasskeyList,
setupNetworkBlocker,
setupUser,
Expand Down Expand Up @@ -34,11 +34,11 @@ test.describe.serial('webhook tests', () => {
setupVirtualAuthenticator(test);
setupNetworkBlocker(test);
setupUser(test, true, false);
loadPasskeyAppend(test);
loadBeforePasskeyAppend(test);
setupWebhooks(test, [WebhookTypes.Create]);

test('successful passkey append on login (+ webhook)', async ({ model }) => {
await model.append.appendPasskey(true);
await model.mfa.submit(true, true);
await model.expectScreen(ScreenNames.PasskeyAppended);

await model.append.confirmAppended();
Expand Down
Loading