From f38d734e4030f446623a6f00a89b3d8d708eebdb Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Tue, 30 May 2023 11:18:39 +0800 Subject: [PATCH] test: add ui tests for webhook management (#3923) --- .../src/tests/ui/user-management.test.ts | 19 +-- .../src/tests/ui/webhooks.test.ts | 158 ++++++++++++++++++ .../integration-tests/src/ui-helpers/index.ts | 20 +++ 3 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 packages/integration-tests/src/tests/ui/webhooks.test.ts create mode 100644 packages/integration-tests/src/ui-helpers/index.ts 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 afa91219c..d9d7df51a 100644 --- a/packages/integration-tests/src/tests/ui/user-management.test.ts +++ b/packages/integration-tests/src/tests/ui/user-management.test.ts @@ -1,8 +1,5 @@ -import { - consolePassword, - consoleUsername, - logtoConsoleUrl as logtoConsoleUrlString, -} from '#src/constants.js'; +import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js'; +import { goToAdminConsole } from '#src/ui-helpers/index.js'; import { appendPathname, formatPhoneNumberToInternational, @@ -18,17 +15,7 @@ describe('user management', () => { const logtoConsoleUrl = new URL(logtoConsoleUrlString); beforeAll(async () => { - await page.goto(logtoConsoleUrl.href); - await page.waitForNavigation({ waitUntil: 'networkidle0' }); - - if (page.url() === new URL('sign-in', logtoConsoleUrl).href) { - await expect(page).toFillForm('form', { - identifier: consoleUsername, - password: consolePassword, - }); - await expect(page).toClick('button[name=submit]'); - await page.waitForNavigation({ waitUntil: 'networkidle0' }); - } + await goToAdminConsole(); }); it('navigates to user management page on clicking sidebar menu', async () => { diff --git a/packages/integration-tests/src/tests/ui/webhooks.test.ts b/packages/integration-tests/src/tests/ui/webhooks.test.ts new file mode 100644 index 000000000..9f7c0f9ac --- /dev/null +++ b/packages/integration-tests/src/tests/ui/webhooks.test.ts @@ -0,0 +1,158 @@ +import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js'; +import { goToAdminConsole } from '#src/ui-helpers/index.js'; +import { appendPathname } from '#src/utils.js'; + +await page.setViewport({ width: 1280, height: 720 }); + +const createWebhookFromWebhooksPage = async () => { + await expect(page).toClick('div[class$=main] div[class$=headline] > button'); + await expect(page).toClick('span[class$=label]', { text: 'Create new account' }); + await expect(page).toClick('span[class$=label]', { text: 'Sign in' }); + await expect(page).toFill('input[name=name]', 'hook_name'); + await expect(page).toFill('input[name=url]', 'https://example.com/webhook'); + await expect(page).toClick('button[type=submit]'); +}; + +describe('webhooks', () => { + const logtoConsoleUrl = new URL(logtoConsoleUrlString); + + beforeAll(async () => { + await goToAdminConsole(); + }); + + it('navigates to webhooks page on clicking sidebar menu', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + await page.waitForNavigation({ waitUntil: 'networkidle0' }); + + await expect(page).toMatchElement( + 'div[class$=main] div[class$=headline] div[class$=titleEllipsis]', + { + text: 'Webhooks', + } + ); + }); + + it('can create a new webhook', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + + await createWebhookFromWebhooksPage(); + + // Go to webhook details page + await page.waitForSelector('div[class$=header] div[class$=metadata] div[class$=title]'); + await expect(page).toMatchElement('div[class$=main] div[class$=metadata] div[class$=title]', { + text: 'hook_name', + }); + const hookId = await page.$eval( + 'div[class$=main] div[class$=metadata] div[class$=row] div:first-of-type', + (element) => element.textContent + ); + if (hookId) { + expect(page.url()).toBe(new URL(`console/webhooks/${hookId}/settings`, logtoConsoleUrl).href); + } + }); + + it('fails to create webhook if no event is provided', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + await page.waitForNavigation({ waitUntil: 'networkidle0' }); + + await expect(page).toClick('div[class$=main] div[class$=headline] > button'); + await expect(page).toFill('input[name=name]', 'hook_name'); + await expect(page).toFill('input[name=url]', 'https://example.com/webhook'); + await expect(page).toClick('button[type=submit]'); + await expect(page).toMatchElement('.ReactModalPortal div[class$=errorMessage]', { + text: 'You have to select at least one event.', + }); + }); + + it('fails to create webhook if endpoint url is not an HTTPS url', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + await page.waitForNavigation({ waitUntil: 'networkidle0' }); + + await expect(page).toClick('div[class$=main] div[class$=headline] > button'); + await expect(page).toClick('span[class$=label]', { text: 'Create new account' }); + await expect(page).toClick('span[class$=label]', { text: 'Sign in' }); + await expect(page).toFill('input[name=name]', 'hook_name'); + await expect(page).toFill('input[name=url]', 'http://example.com/webhook'); + await expect(page).toClick('button[type=submit]'); + await expect(page).toMatchElement('.ReactModalPortal div[class$=errorMessage]', { + text: 'HTTPS format required for security reasons.', + }); + }); + + it('can update webhook details', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + + await createWebhookFromWebhooksPage(); + + await expect(page).toFill('input[name=name]', 'hook_name_updated'); + await expect(page).toFill('input[name=url]', 'https://example.com/new-webhook'); + // Wait for the form to be validated + await page.waitForTimeout(1000); + await expect(page).toClick('form div[class$=actionBar] button:nth-of-type(2)'); + // Wait for the data to be saved + await page.waitForTimeout(1000); + const successToastHandle = await page.waitForSelector('div[class$=success]'); + await expect(successToastHandle).toMatchElement('div[class$=message]', { + text: 'Saved', + }); + }); + + it('can disable or enable a webhook', async () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + 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 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 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', + } + ); + + // 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( + '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 () => { + await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); + 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'); + // Wait for the state to be updated + await page.waitForTimeout(500); + const successToastHandle = await page.waitForSelector('div[class$=success]'); + await expect(successToastHandle).toMatchElement('div[class$=message]', { + 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 new file mode 100644 index 000000000..1a792510b --- /dev/null +++ b/packages/integration-tests/src/ui-helpers/index.ts @@ -0,0 +1,20 @@ +import { + consolePassword, + consoleUsername, + logtoConsoleUrl as logtoConsoleUrlString, +} from '#src/constants.js'; + +export const goToAdminConsole = async () => { + const logtoConsoleUrl = new URL(logtoConsoleUrlString); + await page.goto(logtoConsoleUrl.href); + await page.waitForNavigation({ waitUntil: 'networkidle0' }); + + if (page.url() === new URL('sign-in', logtoConsoleUrl).href) { + await expect(page).toFillForm('form', { + identifier: consoleUsername, + password: consolePassword, + }); + await expect(page).toClick('button[name=submit]'); + await page.waitForNavigation({ waitUntil: 'networkidle0' }); + } +};