diff --git a/package.json b/package.json index 36d658654..d1e15a4c6 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,7 @@ "release:connect-web-js": "./release_connect_web_js.sh", "test:e2e:local": "ts-node-esm ./scripts/e2eTests.ts", "test:e2e:sdk": "lerna run e2e:sdk", - "test:e2e:ui:commitly": "lerna run e2e:ui:commitly", - "test:e2e:ui:nightly": "lerna run e2e:ui:nightly", + "test:e2e:ui": "lerna run e2e:ui", "test:e2e:report": "lerna run e2e:report" }, "devDependencies": { diff --git a/packages/tests-e2e/package.json b/packages/tests-e2e/package.json index d2eff3e25..6cbd6bce9 100644 --- a/packages/tests-e2e/package.json +++ b/packages/tests-e2e/package.json @@ -12,8 +12,7 @@ "url": "git+https://github.com/corbado/javascript.git" }, "scripts": { - "e2e:ui:commitly": "playwright test --config=playwright.config.ui.ts --project=commitly", - "e2e:ui:nightly": "playwright test --config=playwright.config.ui.ts --project=nightly", + "e2e:ui": "playwright test --config=playwright.config.ui.ts", "e2e:report": "npx playwright show-report" }, "bugs": { diff --git a/packages/tests-e2e/src/models/corbado-auth-blocks/LoginInitBlockModel.ts b/packages/tests-e2e/src/models/corbado-auth-blocks/LoginInitBlockModel.ts index 89502a342..dcf2e741d 100644 --- a/packages/tests-e2e/src/models/corbado-auth-blocks/LoginInitBlockModel.ts +++ b/packages/tests-e2e/src/models/corbado-auth-blocks/LoginInitBlockModel.ts @@ -2,6 +2,7 @@ import type { Page } from '@playwright/test'; import { expect } from '@playwright/test'; import type { SocialProviderType } from '../../utils/constants'; +import { repeatSocialLogin, socialLogin } from './socialLogin'; export class LoginInitBlockModel { page: Page; @@ -38,6 +39,14 @@ export class LoginInitBlockModel { await this.page.getByRole('button', { name: 'Continue' }).click(); } + async submitSocialMicrosoft(email: string, password: string) { + await socialLogin(this.page, email, password); + } + + async resubmitSocialMicrosoft() { + await repeatSocialLogin(this.page); + } + submitPasskeyButton() { return this.page.locator('.cb-last-identifier').click(); } diff --git a/packages/tests-e2e/src/models/corbado-auth-blocks/SignupInitBlockModel.ts b/packages/tests-e2e/src/models/corbado-auth-blocks/SignupInitBlockModel.ts index 521d8f2bf..f73a77ad5 100644 --- a/packages/tests-e2e/src/models/corbado-auth-blocks/SignupInitBlockModel.ts +++ b/packages/tests-e2e/src/models/corbado-auth-blocks/SignupInitBlockModel.ts @@ -3,6 +3,7 @@ import { expect } from '@playwright/test'; import type { SocialProviderType } from '../../utils/constants'; import { getRandomIntegerN } from '../../utils/random'; +import { repeatSocialLogin, socialLogin } from './socialLogin'; export class SignupInitBlockModel { page: Page; @@ -53,31 +54,15 @@ export class SignupInitBlockModel { } submitPrimary() { - return this.page.getByRole('button', { name: 'Continue' }).click(); + return this.page.getByRole('button', { name: 'Continue', exact: true }).click(); } - async submitSocialMicrosoft() { - const microsoftEmail = process.env.PLAYWRIGHT_MICROSOFT_EMAIL ?? ''; - const microsoftPassword = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; - - await this.page.getByTitle(`Continue with Microsoft`).click(); - await expect(this.page.getByRole('heading', { level: 1 })).toHaveText('Sign in'); - - await this.page.getByRole('textbox', { name: 'email' }).click(); - await this.page.getByRole('textbox', { name: 'email' }).fill(microsoftEmail); - await expect(this.page.getByRole('textbox', { name: 'email' })).toHaveValue(microsoftEmail); - - await this.page.getByRole('button', { name: 'Next' }).click(); - await expect(this.page.getByRole('heading', { level: 1 })).toHaveText('Enter password'); - - await this.page.getByPlaceholder('Password').click(); - await this.page.getByPlaceholder('Password').fill(microsoftPassword); - await expect(this.page.getByPlaceholder('Password')).toHaveValue(microsoftPassword); - - await this.page.getByRole('button', { name: 'Sign in' }).click(); - await expect(this.page.getByRole('heading', { level: 1 })).toHaveText('Stay signed in?'); + async submitSocialMicrosoft(email: string, password: string) { + await socialLogin(this.page, email, password); + } - await this.page.getByRole('button', { name: 'No' }).click(); + async resubmitSocialMicrosoft() { + await repeatSocialLogin(this.page); } expectErrorMissingUsername(): Promise { diff --git a/packages/tests-e2e/src/models/corbado-auth-blocks/socialLogin.ts b/packages/tests-e2e/src/models/corbado-auth-blocks/socialLogin.ts new file mode 100644 index 000000000..f4d753577 --- /dev/null +++ b/packages/tests-e2e/src/models/corbado-auth-blocks/socialLogin.ts @@ -0,0 +1,30 @@ +import type { Page } from '@playwright/test'; +import { expect } from '@playwright/test'; + +export const socialLogin = async (page: Page, email: string, password: string) => { + await page.getByTitle(`Continue with Microsoft`).click(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Sign in'); + + await page.getByRole('textbox', { name: 'email' }).click(); + await page.getByRole('textbox', { name: 'email' }).fill(email); + await expect(page.getByRole('textbox', { name: 'email' })).toHaveValue(email); + + await page.getByRole('button', { name: 'Next' }).click(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Enter password'); + + await page.getByPlaceholder('Password').click(); + await page.getByPlaceholder('Password').fill(password); + await expect(page.getByPlaceholder('Password')).toHaveValue(password); + + await page.getByRole('button', { name: 'Sign in' }).click(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Stay signed in?'); + + await page.getByRole('button', { name: 'No' }).click(); +}; + +export const repeatSocialLogin = async (page: Page) => { + await page.getByTitle(`Continue with Microsoft`).click(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Let this app access your info? (1 of 1 apps)'); + + await page.getByRole('button', { name: 'Accept' }).click(); +}; diff --git a/packages/tests-e2e/src/ui/corbado-auth-general/socials.spec.ts b/packages/tests-e2e/src/ui/corbado-auth-general/socials.spec.ts index 0b9efc75b..aeb4976f6 100644 --- a/packages/tests-e2e/src/ui/corbado-auth-general/socials.spec.ts +++ b/packages/tests-e2e/src/ui/corbado-auth-general/socials.spec.ts @@ -4,6 +4,7 @@ import { IdentifierType, IdentifierVerification, ScreenNames, + socialOperationTimeout, SocialProviderType, } from '../../utils/constants'; import { @@ -17,13 +18,19 @@ import { test.describe('social logins', () => { let projectId: string; - test.beforeAll(async () => { + // Microsoft social login requires longer timeout + test.use({ + actionTimeout: socialOperationTimeout, + navigationTimeout: socialOperationTimeout, + }); + + test.beforeEach(async () => { projectId = await createProjectNew(); await setComponentConfig( projectId, [ - makeIdentifier(IdentifierType.Email, IdentifierEnforceVerification.Signup, true, [ + makeIdentifier(IdentifierType.Email, IdentifierEnforceVerification.None, true, [ IdentifierVerification.EmailOtp, ]), ], @@ -35,7 +42,7 @@ test.describe('social logins', () => { ); }); - test.afterAll(async () => { + test.afterEach(async () => { await deleteProjectNew(projectId); }); @@ -59,29 +66,102 @@ test.describe('social logins', () => { ); }); - // this covers signup + login - test.skip('signup with socials should be possible (account does not exist)', async ({ model }) => { + test('signup with socials should be possible (account does not exist)', async ({ model }) => { await model.load(projectId, true, 'signup-init'); - await model.signupInit.submitSocialMicrosoft(); + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + + await model.signupInit.submitSocialMicrosoft(email, password); + await model.expectScreen(ScreenNames.PasskeyAppend1); + }); + + test.skip('signup with social should be possible (account exists, social has been linked)', async ({ model }) => { + await model.load(projectId, true, 'signup-init'); + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL_LINKED ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + + await model.signupInit.submitSocialMicrosoft(email, password); + await model.expectScreen(ScreenNames.PasskeyAppend1); + await model.passkeyAppend.startPasskeyOperation(true); await model.expectScreen(ScreenNames.End); await model.logout(); - }); - test.skip('signup with social should be possible (account exists, social has been linked)', async ({ model }) => {}); + await model.load(projectId, true, 'signup-init'); + + await model.signupInit.resubmitSocialMicrosoft(); + // TODO: should successfully log in, but gets redirected to login-init instead. + await model.expectScreen(ScreenNames.End); + }); // in that case only identifier based login should be possible - test.skip('signup with social should not be possible (account exists, social has not been linked)', async ({ - model, - }) => {}); + test('signup with social should not be possible (account exists, social has not been linked)', async ({ model }) => { + await model.load(projectId, true, 'signup-init'); + + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL_UNLINKED ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + + await model.signupInit.fillEmail(email); + await model.signupInit.submitPrimary(); + await model.passkeyAppend.startPasskeyOperation(true); + await model.expectScreen(ScreenNames.End); + await model.logout(); + + await model.load(projectId, true, 'signup-init'); + + await model.signupInit.submitSocialMicrosoft(email, password); + await model.expectScreen(ScreenNames.InitLogin); + }); + + test('login with social should be possible (account does not exist)', async ({ model }) => { + // redirects to passkey append screen + await model.load(projectId, true, 'login-init'); + + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + await model.loginInit.submitSocialMicrosoft(email, password); + + await model.expectScreen(ScreenNames.PasskeyAppend1); + }); + + test('login with social should be possible (account exists, social has been linked)', async ({ model }) => { + await model.load(projectId, true, 'signup-init'); + + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL_LINKED ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + + await model.signupInit.submitSocialMicrosoft(email, password); + await model.expectScreen(ScreenNames.PasskeyAppend1); + await model.passkeyAppend.startPasskeyOperation(true); + await model.expectScreen(ScreenNames.End); + await model.logout(); - test.skip('login with social should be possible (account does not exist)', async ({ model }) => {}); + await model.load(projectId, true, 'login-init'); - test.skip('login with social should be possible (account exists, social has been linked)', async ({ model }) => {}); + await model.signupInit.resubmitSocialMicrosoft(); + await model.expectScreen(ScreenNames.End); + }); // in that case only identifier based login should be possible test.skip('login with social should not be possible (account exists, social has not been linked)', async ({ model, - }) => {}); + }) => { + await model.load(projectId, true, 'signup-init'); + + const email = process.env.PLAYWRIGHT_MICROSOFT_EMAIL_UNLINKED ?? ''; + const password = process.env.PLAYWRIGHT_MICROSOFT_PASSWORD ?? ''; + + await model.signupInit.fillEmail(email); + await model.signupInit.submitPrimary(); + await model.passkeyAppend.startPasskeyOperation(true); + await model.expectScreen(ScreenNames.End); + await model.logout(); + + await model.load(projectId, true, 'login-init'); + + await model.signupInit.submitSocialMicrosoft(email, password); + // TODO: should redirect to login-init screen, but gets successfully logged in insteaad. + await model.expectScreen(ScreenNames.InitLogin); + }); }); diff --git a/packages/tests-e2e/src/utils/constants.ts b/packages/tests-e2e/src/utils/constants.ts index 7e9e6fc2d..acc847690 100644 --- a/packages/tests-e2e/src/utils/constants.ts +++ b/packages/tests-e2e/src/utils/constants.ts @@ -54,8 +54,9 @@ export enum AuthType { export const emailLinkUrlToken = 'UaTwjBJwyDLMGVbR7WHh'; -export const operationTimeout = process.env.CI ? 3000 : 5000; export const totalTimeout = process.env.CI ? 20000 : 30000; +export const operationTimeout = process.env.CI ? 3000 : 5000; +export const socialOperationTimeout = 10000; export const waitAfterLoad = 600; // timeout to reduce flakiness due to repetitive reloads export enum SocialProviderType { diff --git a/playground/react/.env b/playground/react/.env index 8c32df1be..246ae453b 100644 --- a/playground/react/.env +++ b/playground/react/.env @@ -1,6 +1,6 @@ REACT_APP_CORBADO_PROJECT_ID_ManualTesting=pro-3652881945085154854 REACT_APP_CORBADO_PROJECT_ID_LocalDevelopment=pro-2 -# REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.corbado.io -# REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.cloud.corbado-staging.io -REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.corbado-dev.io +REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.cloud.corbado-staging.io +# REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.corbado-dev.io +# REACT_APP_CORBADO_FRONTEND_API_URL_SUFFIX=frontendapi.corbado.io