From 4f5881304ea0e7bcc9fccc7e3a2a7581d5a95712 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Thu, 31 Aug 2023 12:14:19 +0800 Subject: [PATCH] refactor(test): add modal ui test helpers (#4412) --- .../src/tests/ui/connectors/helpers.ts | 23 +-- .../ui/connectors/social-connectors.test.ts | 8 +- .../src/tests/ui/rbac.test.ts | 143 ++++-------------- .../src/tests/ui/user-management.test.ts | 16 +- .../src/tests/ui/webhooks.test.ts | 52 +++---- .../integration-tests/src/ui-helpers/index.ts | 37 ++--- 6 files changed, 93 insertions(+), 186 deletions(-) diff --git a/packages/integration-tests/src/tests/ui/connectors/helpers.ts b/packages/integration-tests/src/tests/ui/connectors/helpers.ts index f3e7f3cc5..71b109b6f 100644 --- a/packages/integration-tests/src/tests/ui/connectors/helpers.ts +++ b/packages/integration-tests/src/tests/ui/connectors/helpers.ts @@ -1,7 +1,11 @@ import { ConnectorType } from '@logto/connector-kit'; import { type Page } from 'puppeteer'; -import { expectConfirmModalAndAct, waitForToast } from '#src/ui-helpers/index.js'; +import { + expectConfirmModalAndAct, + expectModalWithTitle, + waitForToast, +} from '#src/ui-helpers/index.js'; import { passwordlessConnectorTestCases, @@ -38,16 +42,13 @@ export const expectToSelectConnector = async ( page: Page, { groupFactoryId, factoryId, connectorType }: SelectConnectorOption ) => { - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: - connectorType === ConnectorType.Email - ? 'Set up email connector' - : connectorType === ConnectorType.Sms - ? 'Set up SMS connector' - : 'Add Social Connector', - } + await expectModalWithTitle( + page, + connectorType === ConnectorType.Email + ? 'Set up email connector' + : connectorType === ConnectorType.Sms + ? 'Set up SMS connector' + : 'Add Social Connector' ); if (groupFactoryId) { diff --git a/packages/integration-tests/src/tests/ui/connectors/social-connectors.test.ts b/packages/integration-tests/src/tests/ui/connectors/social-connectors.test.ts index 78c47ebea..5cab1d0c0 100644 --- a/packages/integration-tests/src/tests/ui/connectors/social-connectors.test.ts +++ b/packages/integration-tests/src/tests/ui/connectors/social-connectors.test.ts @@ -7,6 +7,7 @@ import { goToAdminConsole, expectToSaveChanges, waitForToast, + expectModalWithTitle, } from '#src/ui-helpers/index.js'; import { expectNavigation, appendPathname } from '#src/utils.js'; @@ -53,12 +54,7 @@ describe('social connectors', () => { text: 'Add Social Connector', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Add Social Connector', - } - ); + await expectModalWithTitle(page, 'Add Social Connector'); // Close modal await page.keyboard.press('Escape'); diff --git a/packages/integration-tests/src/tests/ui/rbac.test.ts b/packages/integration-tests/src/tests/ui/rbac.test.ts index 039a9586f..6be2d4f0e 100644 --- a/packages/integration-tests/src/tests/ui/rbac.test.ts +++ b/packages/integration-tests/src/tests/ui/rbac.test.ts @@ -1,7 +1,9 @@ import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js'; import { expectConfirmModalAndAct, + expectModalWithTitle, expectToClickDetailsPageOption, + expectToClickModalAction, goToAdminConsole, waitForToast, } from '#src/ui-helpers/index.js'; @@ -49,21 +51,14 @@ describe('RBAC', () => { text: 'Create API Resource', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Create API Resource', - } - ); + await expectModalWithTitle(page, 'Create API Resource'); await expect(page).toFillForm('.ReactModalPortal form', { name: apiResourceName, indicator: apiResourceIndicator, }); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button[type=submit] span', { - text: 'Create API Resource', - }); + await expectToClickModalAction(page, 'Create API Resource'); await waitForToast(page, { text: `The API resource ${apiResourceName} has been successfully created`, @@ -83,21 +78,14 @@ describe('RBAC', () => { text: 'Create Permission', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Create permission', - } - ); + await expectModalWithTitle(page, 'Create permission'); await expect(page).toFillForm('.ReactModalPortal form', { name: permissionName, description: permissionDescription, }); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button[type=submit] span', { - text: 'Create permission', - }); + await expectToClickModalAction(page, 'Create permission'); await waitForToast(page, { text: `The permission ${permissionName} has been successfully created`, @@ -121,25 +109,15 @@ describe('RBAC', () => { it('create a user for rbac testing', async () => { await expect(page).toClick('div[class$=headline] button span', { text: 'Add User' }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Add User', - } - ); + await expectModalWithTitle(page, 'Add User'); await expect(page).toFillForm('.ReactModalPortal form', { username: rbacTestUsername, }); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button[type=submit] span', { - text: 'Add User', - }); + await expectToClickModalAction(page, 'Add User'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { text: 'This user has been successfully created' } - ); + await expectModalWithTitle(page, 'This user has been successfully created'); await page.keyboard.press('Escape'); }); @@ -160,12 +138,7 @@ describe('RBAC', () => { text: 'Create Role', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Create Role', - } - ); + await expectModalWithTitle(page, 'Create Role'); await expect(page).toFillForm('.ReactModalPortal form', { name: roleName, @@ -187,24 +160,14 @@ describe('RBAC', () => { } ); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button[type=submit] span', { - text: 'Create Role', - }); + await expectToClickModalAction(page, 'Create Role'); await waitForToast(page, { text: `The role ${roleName} has been successfully created.`, }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Assign users', - } - ); - - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Skip for now', - }); + await expectModalWithTitle(page, 'Assign users'); + await expectToClickModalAction(page, 'Skip for now'); await expect(page).toMatchElement('div[class$=header] div[class$=info] div[class$=name]', { text: roleName, @@ -222,15 +185,9 @@ describe('RBAC', () => { ); await expect(permissionRow).toClick('td[class$=deleteColumn] button'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Reminder', - } - ); - - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Remove', + await expectConfirmModalAndAct(page, { + title: 'Reminder', + actionText: 'Remove', }); await waitForToast(page, { @@ -243,12 +200,7 @@ describe('RBAC', () => { text: 'Assign Permissions', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Assign permissions', - } - ); + await expectModalWithTitle(page, 'Assign permissions'); await expect(page).toClick( '.ReactModalPortal div[class$=resourceItem] div[class$=title] div[class$=name]', @@ -264,9 +216,7 @@ describe('RBAC', () => { } ); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Assign Permissions', - }); + await expectToClickModalAction(page, 'Assign Permissions'); await waitForToast(page, { text: 'The selected permissions were successfully assigned to this role', @@ -286,12 +236,7 @@ describe('RBAC', () => { text: 'Assign Users', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Assign users', - } - ); + await expectModalWithTitle(page, 'Assign users'); await expect(page).toClick( '.ReactModalPortal div[class$=roleUsersTransfer] div[class$=item] div[class$=title]', @@ -299,10 +244,7 @@ describe('RBAC', () => { text: rbacTestUsername, } ); - - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Assign users', - }); + await expectToClickModalAction(page, 'Assign users'); await waitForToast(page, { text: 'The selected users were successfully assigned to this role', @@ -338,15 +280,9 @@ describe('RBAC', () => { // Click remove button await expect(roleRow).toClick('td:last-of-type button'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Reminder', - } - ); - - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Remove', + await expectConfirmModalAndAct(page, { + title: 'Reminder', + actionText: 'Remove', }); await waitForToast(page, { @@ -359,12 +295,7 @@ describe('RBAC', () => { text: 'Assign Roles', }); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: `Assign roles to ${rbacTestUsername}`, - } - ); + await expectModalWithTitle(page, `Assign roles to ${rbacTestUsername}`); await expect(page).toClick( '.ReactModalPortal div[class$=rolesTransfer] div[class$=item] div[class$=name]', @@ -373,9 +304,7 @@ describe('RBAC', () => { } ); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Assign roles', - }); + await expectToClickModalAction(page, 'Assign roles'); await waitForToast(page, { text: 'Successfully assigned role(s)', @@ -458,16 +387,7 @@ describe('RBAC', () => { ); await expect(permissionRow).toClick('td[class$=deleteColumn] button'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Reminder', - } - ); - - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Delete', - }); + await expectConfirmModalAndAct(page, { title: 'Reminder', actionText: 'Delete' }); await waitForToast(page, { text: `The permission "${permissionName}" was successfully deleted.`, @@ -477,18 +397,11 @@ describe('RBAC', () => { it('delete api resource', async () => { await expectToClickDetailsPageOption(page, 'Delete'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'Reminder', - } - ); + await expectModalWithTitle(page, 'Reminder'); await expect(page).toFill('.ReactModalPortal input', apiResourceName); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: 'Delete', - }); + await expectToClickModalAction(page, 'Delete'); await waitForToast(page, { text: `The API Resource ${apiResourceName} has been successfully deleted`, diff --git a/packages/integration-tests/src/tests/ui/user-management.test.ts b/packages/integration-tests/src/tests/ui/user-management.test.ts index 3ef039eec..ce86aaa7d 100644 --- a/packages/integration-tests/src/tests/ui/user-management.test.ts +++ b/packages/integration-tests/src/tests/ui/user-management.test.ts @@ -4,6 +4,8 @@ import { expectToSaveChanges, waitForToast, expectToDiscardChanges, + expectModalWithTitle, + expectToClickModalAction, } from '#src/ui-helpers/index.js'; import { appendPathname, @@ -44,14 +46,10 @@ describe('user management', () => { }); await expect(page).toClick('button[type=submit]'); await page.waitForSelector('div[class$=infoLine'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', - { - text: 'This user has been successfully created', - } - ); + await expectModalWithTitle(page, 'This user has been successfully created'); + // Go to user details page - await expect(page).toClick('div.ReactModalPortal div[class$=footer] button:first-of-type'); + await expectToClickModalAction(page, 'Check user detail'); await expect(page).toMatchElement('div[class$=main] div[class$=metadata] div[class$=title]', { text: 'jdoe@gmail.com', }); @@ -131,7 +129,7 @@ describe('user management', () => { await page.waitForSelector('div[class$=infoLine'); // Go to the user details page - await expect(page).toClick('div.ReactModalPortal div[class$=footer] button:nth-of-type(1)'); + await expectToClickModalAction(page, 'Check user detail'); await expect(page).toMatchElement('div[class$=main] div[class$=metadata] div[class$=title]', { text: username, }); @@ -183,7 +181,7 @@ describe('user management', () => { await expect(page).toMatchElement('.ReactModalPortal div[class$=medium] div[class$=content]', { text: 'User needs to have at least one of the sign-in identifiers (username, email, phone number or social) to sign in. Are you sure you want to continue?', }); - await expect(page).toClick('div.ReactModalPortal div[class$=footer] button:nth-of-type(2)'); + await expectToClickModalAction(page, 'Confirm'); // After all identifiers, top userinfo card shows 'Unnamed' as the title await expect(page).toMatchElement('div[class$=main] div[class$=metadata] div[class$=title]', { text: 'Unnamed', diff --git a/packages/integration-tests/src/tests/ui/webhooks.test.ts b/packages/integration-tests/src/tests/ui/webhooks.test.ts index 3dfb27348..dc09065e1 100644 --- a/packages/integration-tests/src/tests/ui/webhooks.test.ts +++ b/packages/integration-tests/src/tests/ui/webhooks.test.ts @@ -1,5 +1,13 @@ import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js'; -import { goToAdminConsole, expectToSaveChanges, waitForToast } from '#src/ui-helpers/index.js'; +import { + goToAdminConsole, + expectToSaveChanges, + waitForToast, + expectToClickModalAction, + expectToClickDetailsPageOption, + expectModalWithTitle, + expectConfirmModalAndAct, +} from '#src/ui-helpers/index.js'; import { appendPathname, expectNavigation } from '#src/utils.js'; await page.setViewport({ width: 1280, height: 720 }); @@ -93,38 +101,28 @@ describe('webhooks', () => { await createWebhookFromWebhooksPage(); // Disable webhook - await expect(page).toClick('div[class$=header] >div:nth-of-type(2) button'); - // Wait for the menu to be opened - await page.waitForTimeout(500); - await expect(page).toClick( - '.ReactModalPortal div[class$=dropdownContainer] div[role=menuitem]:nth-of-type(1)' - ); + await expectToClickDetailsPageOption(page, 'Disable webhook'); + await expectModalWithTitle(page, 'Reminder'); await expect(page).toMatchElement('.ReactModalPortal div[class$=content] div[class$=content]', { text: 'Are you sure you want to reactivate this webhook? Doing so will not send HTTP request to endpoint URL.', }); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button:last-of-type'); - // Wait for the state to be updated - await page.waitForTimeout(500); + await expectToClickModalAction(page, 'Disable webhook'); + await expect(page).toMatchElement( 'div[class$=header] div[class$=metadata] div:nth-of-type(2) div[class$=outlined] div:nth-of-type(2)', { text: 'Not in use', + timeout: 1000, } ); - // Enable webhook - await expect(page).toClick('div[class$=header] >div:nth-of-type(2) button'); - // Wait for the menu to be opened - await page.waitForTimeout(500); - await expect(page).toClick( - '.ReactModalPortal div[class$=dropdownContainer] div[role=menuitem]:nth-of-type(1)' - ); - // Wait for the state to be updated - await page.waitForTimeout(500); - const stateDiv = await page.waitForSelector( + // Reactivate webhook + await expectToClickDetailsPageOption(page, 'Reactivate webhook'); + + // Wait for the active webhook state info to appear + await page.waitForSelector( 'div[class$=header] div[class$=metadata] div:nth-of-type(2) div[class$=state]' ); - expect(stateDiv).toBeTruthy(); }); it('can regenerate signing key for a webhook', async () => { @@ -132,13 +130,11 @@ describe('webhooks', () => { await createWebhookFromWebhooksPage(); await expect(page).toClick('button[class$=regenerateButton]'); - await expect(page).toMatchElement( - '.ReactModalPortal div[class$=content] div[class$=titleEllipsis]', - { - text: 'Regenerate signing key', - } - ); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button:last-of-type'); + await expectConfirmModalAndAct(page, { + title: 'Regenerate signing key', + actionText: 'Regenerate', + }); + await waitForToast(page, { text: 'Signing key has been regenerated.' }); }); }); diff --git a/packages/integration-tests/src/ui-helpers/index.ts b/packages/integration-tests/src/ui-helpers/index.ts index 98765f964..877bd8197 100644 --- a/packages/integration-tests/src/ui-helpers/index.ts +++ b/packages/integration-tests/src/ui-helpers/index.ts @@ -45,9 +45,7 @@ export const expectUnsavedChangesAlert = async (page: Page) => { '.ReactModalPortal div[class$=content]::-p-text(You have made some changes. Are you sure you want to leave this page?)' ); - await expect(page).toClick('.ReactModalPortal div[class$=footer] button', { - text: 'Stay on Page', - }); + await expectToClickModalAction(page, 'Stay on Page'); }; export const expectToSaveChanges = async (page: Page) => { @@ -89,27 +87,32 @@ export const expectToClickDetailsPageOption = async (page: Page, optionText: str ); }; -type ExpectConfirmModalAndActOptions = { - title?: string | RegExp; - actionText?: string | RegExp; -}; - -export const expectConfirmModalAndAct = async ( - page: Page, - { title, actionText }: ExpectConfirmModalAndActOptions -) => { +export const expectModalWithTitle = async (page: Page, title: string | RegExp) => { await expect(page).toMatchElement( '.ReactModalPortal div[class$=header] div[class$=titleEllipsis]', { text: title, } ); +}; - if (actionText) { - await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { - text: actionText, - }); - } +export const expectToClickModalAction = async (page: Page, actionText: string | RegExp) => { + await expect(page).toClick('.ReactModalPortal div[class$=footer] button span', { + text: actionText, + }); +}; + +type ExpectConfirmModalAndActOptions = { + title: string | RegExp; + actionText: string | RegExp; +}; + +export const expectConfirmModalAndAct = async ( + page: Page, + { title, actionText }: ExpectConfirmModalAndActOptions +) => { + await expectModalWithTitle(page, title); + await expectToClickModalAction(page, actionText); }; export const expectToClickNavTab = async (page: Page, tab: string) => {