From 092b3d7aea9bb593716f4f263d53fe7fbf863cf8 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Fri, 15 Sep 2023 00:50:13 +0800 Subject: [PATCH 1/2] refactor: ui -> experience (part 1) --- packages/integration-tests/package.json | 2 +- .../{flows => experience}/bootstrap.test.ts | 14 ++-- .../password-policy.test.ts | 52 ++++++------- .../{expect-flows.ts => expect-experience.ts} | 76 +++++++++---------- 4 files changed, 72 insertions(+), 72 deletions(-) rename packages/integration-tests/src/tests/{flows => experience}/bootstrap.test.ts (79%) rename packages/integration-tests/src/tests/{flows => experience}/password-policy.test.ts (67%) rename packages/integration-tests/src/ui-helpers/{expect-flows.ts => expect-experience.ts} (61%) diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index b857b0c22..2b805330f 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -14,7 +14,7 @@ "test:only": "NODE_OPTIONS=--experimental-vm-modules jest", "test": "pnpm build && pnpm test:api && pnpm test:experience && pnpm test:console", "test:api": "pnpm test:only -i ./lib/tests/api/", - "test:experience": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/flows/", + "test:experience": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/experience/", "test:console": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/console/", "lint": "eslint --ext .ts src", "lint:report": "pnpm lint --format json --output-file report.json", diff --git a/packages/integration-tests/src/tests/flows/bootstrap.test.ts b/packages/integration-tests/src/tests/experience/bootstrap.test.ts similarity index 79% rename from packages/integration-tests/src/tests/flows/bootstrap.test.ts rename to packages/integration-tests/src/tests/experience/bootstrap.test.ts index 336e69b83..1a8e1c72f 100644 --- a/packages/integration-tests/src/tests/flows/bootstrap.test.ts +++ b/packages/integration-tests/src/tests/experience/bootstrap.test.ts @@ -3,7 +3,7 @@ import { SignInMode, SignInIdentifier, ConnectorType } from '@logto/schemas'; import { updateSignInExperience } from '#src/api/sign-in-experience.js'; import { demoAppUrl } from '#src/constants.js'; import { clearConnectorsByTypes } from '#src/helpers/connector.js'; -import ExpectFlows from '#src/ui-helpers/expect-flows.js'; +import ExpectExperience from '#src/ui-helpers/expect-experience.js'; const credentials = { username: 'test_bootstrap', @@ -42,19 +42,19 @@ describe('smoke testing on the demo app', () => { }); it('should be able to create a new account with a credential preset', async () => { - const journey = new ExpectFlows(await browser.newPage()); + const experience = new ExpectExperience(await browser.newPage()); // Open the demo app and navigate to the register page - await journey.startWith(demoAppUrl, 'register'); - await journey.toFillInput('identifier', credentials.username, { submit: true }); + await experience.startWith(demoAppUrl, 'register'); + await experience.toFillInput('identifier', credentials.username, { submit: true }); // Simple password tests - journey.toBeAt('register/password'); - await journey.toFillPasswords( + experience.toBeAt('register/password'); + await experience.toFillPasswords( [credentials.pwnedPassword, 'simple password'], credentials.password ); - await journey.verifyThenEnd(); + await experience.verifyThenEnd(); }); }); diff --git a/packages/integration-tests/src/tests/flows/password-policy.test.ts b/packages/integration-tests/src/tests/experience/password-policy.test.ts similarity index 67% rename from packages/integration-tests/src/tests/flows/password-policy.test.ts rename to packages/integration-tests/src/tests/experience/password-policy.test.ts index 6387e8fc7..4e8ce8792 100644 --- a/packages/integration-tests/src/tests/flows/password-policy.test.ts +++ b/packages/integration-tests/src/tests/experience/password-policy.test.ts @@ -5,7 +5,7 @@ import { ConnectorType, SignInIdentifier, SignInMode } from '@logto/schemas'; import { updateSignInExperience } from '#src/api/sign-in-experience.js'; import { demoAppUrl } from '#src/constants.js'; import { clearConnectorsByTypes, setEmailConnector } from '#src/helpers/connector.js'; -import ExpectFlows from '#src/ui-helpers/expect-flows.js'; +import ExpectExperience from '#src/ui-helpers/expect-experience.js'; import { waitFor } from '#src/utils.js'; describe('password policy', () => { @@ -60,21 +60,21 @@ describe('password policy', () => { }); it('should work for username + password', async () => { - const journey = new ExpectFlows(await browser.newPage(), { forgotPassword: true }); + const experience = new ExpectExperience(await browser.newPage(), { forgotPassword: true }); // Open the demo app and navigate to the register page - await journey.startWith(demoAppUrl, 'register'); - await journey.toFillInput('identifier', username, { submit: true }); + await experience.startWith(demoAppUrl, 'register'); + await experience.toFillInput('identifier', username, { submit: true }); // Password tests - journey.toBeAt('register/password'); - await journey.toFillPasswords( + experience.toBeAt('register/password'); + await experience.toFillPasswords( ...invalidPasswords, [username + 'A', /product context .* personal information/], username + 'ABCD_ok' ); - await journey.verifyThenEnd(); + await experience.verifyThenEnd(); }); it('should work for email + password', async () => { @@ -86,56 +86,56 @@ describe('password policy', () => { verify: true, }, }); - const journey = new ExpectFlows(await browser.newPage(), { forgotPassword: true }); + const experience = new ExpectExperience(await browser.newPage(), { forgotPassword: true }); // Open the demo app and navigate to the register page - await journey.startWith(demoAppUrl, 'register'); + await experience.startWith(demoAppUrl, 'register'); // Complete verification code flow - await journey.toFillInput('identifier', email, { submit: true }); - await journey.toCompleteVerification('register'); + await experience.toFillInput('identifier', email, { submit: true }); + await experience.toCompleteVerification('register'); // Wait for the password page to load await waitFor(100); - journey.toBeAt('continue/password'); - await journey.toFillPasswords( + experience.toBeAt('continue/password'); + await experience.toFillPasswords( ...invalidPasswords, [emailName, 'personal information'], emailName + 'ABCD@# $' ); - await journey.verifyThenEnd(); + await experience.verifyThenEnd(); }); it('should work for forgot password', async () => { - const journey = new ExpectFlows(await browser.newPage(), { forgotPassword: true }); + const experience = new ExpectExperience(await browser.newPage(), { forgotPassword: true }); // Open the demo app and navigate to the register page - await journey.startWith(demoAppUrl, 'sign-in'); + await experience.startWith(demoAppUrl, 'sign-in'); // Click the forgot password link - await journey.toFillInput('identifier', email, { submit: true }); - await journey.toClick('a', 'Forgot your password'); + await experience.toFillInput('identifier', email, { submit: true }); + await experience.toClick('a', 'Forgot your password'); // Submit to continue - await journey.toClickSubmit(); + await experience.toClickSubmit(); // Complete verification code flow - await journey.toCompleteVerification('forgot-password'); + await experience.toCompleteVerification('forgot-password'); // Wait for the password page to load await waitFor(100); - journey.toBeAt('forgot-password/reset'); - await journey.toFillPasswords( + experience.toBeAt('forgot-password/reset'); + await experience.toFillPasswords( ...invalidPasswords, [emailName, 'personal information'], [emailName + 'ABCD@# $', 'be the same as'], emailName + 'ABCD135' ); - journey.toBeAt('sign-in'); - await journey.toFillInput('identifier', email, { submit: true }); - await journey.toFillInput('password', emailName + 'ABCD135', { submit: true }); - await journey.verifyThenEnd(); + experience.toBeAt('sign-in'); + await experience.toFillInput('identifier', email, { submit: true }); + await experience.toFillInput('password', emailName + 'ABCD135', { submit: true }); + await experience.verifyThenEnd(); }); }); diff --git a/packages/integration-tests/src/ui-helpers/expect-flows.ts b/packages/integration-tests/src/ui-helpers/expect-experience.ts similarity index 61% rename from packages/integration-tests/src/ui-helpers/expect-flows.ts rename to packages/integration-tests/src/ui-helpers/expect-experience.ts index 84dbd8d8a..9224f1d9d 100644 --- a/packages/integration-tests/src/ui-helpers/expect-flows.ts +++ b/packages/integration-tests/src/ui-helpers/expect-experience.ts @@ -10,17 +10,17 @@ const demoAppUrl = appendPath(new URL(logtoUrl), 'demo-app'); /** Remove the query string together with the `?` from a URL string. */ const stripQuery = (url: string) => url.split('?')[0]; -export type FlowsType = 'sign-in' | 'register' | 'continue' | 'forgot-password'; +export type ExperienceType = 'sign-in' | 'register' | 'continue' | 'forgot-password'; -export type FlowsPath = - | FlowsType - | `${FlowsType}/password` - | `${FlowsType}/verify` - | `${FlowsType}/verification-code` +export type ExperiencePath = + | ExperienceType + | `${ExperienceType}/password` + | `${ExperienceType}/verify` + | `${ExperienceType}/verification-code` | `forgot-password/reset`; -export type ExpectFlowsOptions = { - /** The URL of the flows endpoint. */ +export type ExpectExperienceOptions = { + /** The URL of the experience endpoint. */ endpoint?: URL; /** * Whether the forgot password flow is enabled. @@ -30,30 +30,30 @@ export type ExpectFlowsOptions = { forgotPassword?: boolean; }; -type OngoingFlows = { - type: FlowsType; +type OngoingExperience = { + type: ExperienceType; initialUrl: URL; }; /** * A class that provides: * - * - A set of methods to navigate to a specific page for a flows. - * - A set of methods to assert the state of a flows and its side effects. + * - A set of methods to navigate to a specific page for a experience. + * - A set of methods to assert the state of a experience and its side effects. */ -export default class ExpectFlows extends ExpectPage { - readonly options: Required; +export default class ExpectExperience extends ExpectPage { + readonly options: Required; - protected get flowsType() { + protected get experienceType() { if (this.#ongoing === undefined) { - return this.throwNoOngoingFlowsError(); + return this.throwNoOngoingExperienceError(); } return this.#ongoing.type; } - #ongoing?: OngoingFlows; + #ongoing?: OngoingExperience; - constructor(thePage = global.page, options: ExpectFlowsOptions = {}) { + constructor(thePage = global.page, options: ExpectExperienceOptions = {}) { super(thePage); this.options = { endpoint: new URL(logtoUrl), @@ -63,16 +63,16 @@ export default class ExpectFlows extends ExpectPage { } /** - * Start flows with the given initial URL. Expect the initial URL is protected by Logto, and - * navigate to the flows sign-in page if unauthenticated. + * Start experience with the given initial URL. Expect the initial URL is protected by Logto, and + * navigate to the experience sign-in page if unauthenticated. * - * If the flows can be started, the instance will be marked as ongoing. + * If the experience can be started, the instance will be marked as ongoing. * - * @param initialUrl The initial URL to start the flows with. - * @param type The type of flows to expect. If it's `register`, it will try to click the "Create + * @param initialUrl The initial URL to start the experience with. + * @param type The type of experience to expect. If it's `register`, it will try to click the "Create * account" link on the sign-in page. */ - async startWith(initialUrl = demoAppUrl, type: FlowsType = 'sign-in') { + async startWith(initialUrl = demoAppUrl, type: ExperienceType = 'sign-in') { await this.toStart(initialUrl); this.toBeAt('sign-in'); @@ -85,14 +85,14 @@ export default class ExpectFlows extends ExpectPage { } /** - * Ensure the flows is ongoing and the page is at the initial URL; then try to click the "sign out" + * Ensure the experience is ongoing and the page is at the initial URL; then try to click the "sign out" * button (case-insensitive) and close the page. * - * It will clear the ongoing flows if the flows is ended successfully. + * It will clear the ongoing experience if the experience is ended successfully. */ async verifyThenEnd() { if (this.#ongoing === undefined) { - return this.throwNoOngoingFlowsError(); + return this.throwNoOngoingExperienceError(); } this.toMatchUrl(this.#ongoing.initialUrl); @@ -103,22 +103,22 @@ export default class ExpectFlows extends ExpectPage { } /** - * Assert the page is at the given flows path. + * Assert the page is at the given experience path. * - * @param pathname The flows path to assert. + * @param pathname The experience path to assert. */ - toBeAt(pathname: FlowsPath) { + toBeAt(pathname: ExperiencePath) { const stripped = stripQuery(this.page.url()); - expect(stripped).toBe(this.buildFlowsUrl(pathname).href); + expect(stripped).toBe(this.buildExperienceUrl(pathname).href); } /** * Assert the page is at the verification code page and fill the verification code input with the * code from Logto database. * - * @param type The type of flows to expect. + * @param type The type of experience to expect. */ - async toCompleteVerification(type: FlowsType) { + async toCompleteVerification(type: ExperienceType) { this.toBeAt(`${type}/verification-code`); const { code } = await readVerificationCode(); @@ -140,7 +140,7 @@ export default class ExpectFlows extends ExpectPage { * "simple password" (case-insensitive), and the second password is expected to be accepted. * * ```ts - * await journey.toFillPasswords( + * await experience.toFillPasswords( * [credentials.pwnedPassword, 'simple password'], * credentials.password, * ); @@ -175,12 +175,12 @@ export default class ExpectFlows extends ExpectPage { } } - /** Build a full flows URL from a pathname. */ - protected buildFlowsUrl(pathname = '') { + /** Build a full experience URL from a pathname. */ + protected buildExperienceUrl(pathname = '') { return appendPath(this.options.endpoint, pathname); } - protected throwNoOngoingFlowsError() { - return this.throwError('The flows has not started yet. Use `startWith` to start the flows.'); + protected throwNoOngoingExperienceError() { + return this.throwError('The experience has not started yet. Use `startWith` to start the experience.'); } } From ea6b475a72848f41331ae511bc7d61f96cec779b Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Fri, 15 Sep 2023 01:02:09 +0800 Subject: [PATCH 2/2] chore: fix lint errors --- .../integration-tests/src/ui-helpers/expect-experience.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/integration-tests/src/ui-helpers/expect-experience.ts b/packages/integration-tests/src/ui-helpers/expect-experience.ts index 9224f1d9d..a18ea6bc2 100644 --- a/packages/integration-tests/src/ui-helpers/expect-experience.ts +++ b/packages/integration-tests/src/ui-helpers/expect-experience.ts @@ -181,6 +181,8 @@ export default class ExpectExperience extends ExpectPage { } protected throwNoOngoingExperienceError() { - return this.throwError('The experience has not started yet. Use `startWith` to start the experience.'); + return this.throwError( + 'The experience has not started yet. Use `startWith` to start the experience.' + ); } }