From c1ae872f487fa18440de047f8f7137b5f1dba49b Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Mon, 19 Jun 2023 18:00:38 +0800 Subject: [PATCH] chore(test): add multi-tenancy UI integration test cases --- .../src/tests/ui-cloud/operations.ts | 94 +++++++++++ .../src/tests/ui-cloud/smoke.test.ts | 158 +++++++++++------- packages/integration-tests/tsconfig.json | 3 +- 3 files changed, 189 insertions(+), 66 deletions(-) create mode 100644 packages/integration-tests/src/tests/ui-cloud/operations.ts diff --git a/packages/integration-tests/src/tests/ui-cloud/operations.ts b/packages/integration-tests/src/tests/ui-cloud/operations.ts new file mode 100644 index 000000000..282e324fc --- /dev/null +++ b/packages/integration-tests/src/tests/ui-cloud/operations.ts @@ -0,0 +1,94 @@ +import { type Page } from 'puppeteer'; + +export const onboardingWelcome = async (page: Page) => { + // Select the project type option + await page.click('div[role=radio]:has(input[name=project][value=personal])'); + + // Select the deployment type option + await page.click('div[role=radio]:has(input[name=deploymentType][value=open-source])'); + + // Click the next button + await page.click('div[class$=actions] button:first-child'); +}; + +export const onboardingUserSurvey = async (page: Page) => { + // Wait for the sie config to load + await page.waitForTimeout(1000); + + // Select the first reason option + await page.click('div[role=button][class$=item]'); + + // Click the next button + await page.click('div[class$=actions] button:first-child'); +}; + +export const onboardingSieConfig = async (page: Page) => { + // Wait for the sie config to load + await page.waitForTimeout(1000); + + // Select username as the identifier + await page.click('div[role=radio]:has(input[name=identifier][value=username])'); + + // Click the finish button + await page.click('div[class$=continueActions] button:last-child'); +}; + +export const onboardingFinish = async (page: Page) => { + // Wait for the sie config to load + await page.waitForTimeout(1000); + + // Click the enter ac button + await page.click('div[class$=content] >button'); + + // Wait for the admin console to load + await page.waitForNavigation({ waitUntil: 'networkidle0' }); +}; + +export const openTenantDropdown = async (page: Page) => { + // Click 'current tenant card' locates in topbar + const currentTenantCard = await page.waitForSelector( + 'div[class$=topbar] > div[class$=currentTenantCard][role=button]:has(div[class$=name])' + ); + await currentTenantCard?.click(); +}; + +export const openCreateTenantModal = async (page: Page) => { + const createTenantButton = await page.waitForSelector( + 'div[class$=ReactModalPortal] div[class$=dropdownContainer] div[class$=dropdown] div[class$=createTenantButton][role=button]' + ); + await createTenantButton?.click(); +}; + +export const fillAndCreateTenant = async (page: Page, tenantName: string) => { + // Create tenant with name 'new-tenant' and tag 'production' + await page.waitForTimeout(500); + await page.waitForSelector( + 'div[class$=ReactModalPortal] div[class*=card][class$=medium] input[type=text][name=name]' + ); + await page.waitForSelector( + 'div[class$=ReactModalPortal] div[class*=radioGroup][class$=small] div[class*=radio][class$=small][role=radio] > div[class$=content]:has(input[value=production])' + ); + await page.type( + 'div[class$=ReactModalPortal] div[class*=card][class$=medium] input[type=text][name=name]', + tenantName + ); + await page.click( + 'div[class$=ReactModalPortal] div[class*=radioGroup][class$=small] div[class*=radio][class$=small][role=radio] > div[class$=content]:has(input[value=production])' + ); + + // Click create button + await page.waitForTimeout(500); + await page.click( + 'div[class$=ReactModalPortal] div[class*=card][class$=medium] div[class$=footer] button[type=submit]' + ); +}; + +export const createNewTenant = async (page: Page, tenantName: string) => { + await page.waitForTimeout(500); + await openTenantDropdown(page); + + await page.waitForTimeout(500); + await openCreateTenantModal(page); + + await fillAndCreateTenant(page, tenantName); +}; diff --git a/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts b/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts index d329d3228..934f1894d 100644 --- a/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts +++ b/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts @@ -5,6 +5,17 @@ import { setDefaultOptions } from 'expect-puppeteer'; import { logtoCloudUrl as logtoCloudUrlString, logtoConsoleUrl } from '#src/constants.js'; import { generatePassword } from '#src/utils.js'; +import { + onboardingWelcome, + onboardingUserSurvey, + onboardingSieConfig, + onboardingFinish, + createNewTenant, + fillAndCreateTenant, + openTenantDropdown, + openCreateTenantModal, +} from './operations.js'; + await page.setViewport({ width: 1280, height: 720 }); setDefaultOptions({ timeout: 5000 }); @@ -51,16 +62,7 @@ describe('smoke testing for cloud', () => { }); it('can complete the onboarding welcome process and enter the user survey page', async () => { - // Select the project type option - await expect(page).toClick('div[role=radio]:has(input[name=project][value=personal])'); - - // Select the deployment type option - await expect(page).toClick( - 'div[role=radio]:has(input[name=deploymentType][value=open-source])' - ); - - // Click the next button - await expect(page).toClick('div[class$=actions] button:first-child'); + await onboardingWelcome(page); // Wait for the next page to load await expect(page).toMatchElement('div[class$=content] div[class$=title]', { @@ -71,11 +73,7 @@ describe('smoke testing for cloud', () => { }); it('can complete the onboarding user survey process and enter the sie page', async () => { - // Select the first reason option - await expect(page).toClick('div[role=button][class$=item]'); - - // Click the next button - await expect(page).toClick('div[class$=actions] button:first-child'); + await onboardingUserSurvey(page); // Wait for the next page to load await expect(page).toMatchElement('div[class$=config] div[class$=title]', { @@ -86,14 +84,7 @@ describe('smoke testing for cloud', () => { }); it('can complete the sie configuration process and enter the congrats page', async () => { - // Wait for the sie config to load - await page.waitForTimeout(1000); - - // Select username as the identifier - await expect(page).toClick('div[role=radio]:has(input[name=identifier][value=username])'); - - // Click the finish button - await expect(page).toClick('div[class$=continueActions] button:last-child'); + await onboardingSieConfig(page); // Wait for the next page to load await expect(page).toMatchElement('div[class$=content] div[class$=title]', { @@ -104,11 +95,7 @@ describe('smoke testing for cloud', () => { }); it('can complete the onboarding process and enter the admin console', async () => { - // Click the enter ac button - await expect(page).toClick('div[class$=content] >button'); - - // Wait for the admin console to load - await page.waitForNavigation({ waitUntil: 'networkidle0' }); + await onboardingFinish(page); const mainContent = await page.waitForSelector('div[class$=main]:has(div[class$=title])'); await expect(mainContent).toMatchElement('div[class$=title]', { text: 'Something to explore to help you succeed', @@ -118,41 +105,8 @@ describe('smoke testing for cloud', () => { }); it('can create a new tenant using tenant dropdown', async () => { - // Click 'current tenant card' locates in topbar - const currentTenantCard = await page.waitForSelector( - 'div[class$=topbar] > div[class$=currentTenantCard][role=button]:has(div[class$=name])' - ); - await expect(currentTenantCard).toMatchElement('div[class$=name]', { text: 'My Project' }); - await currentTenantCard.click(); - - await page.waitForTimeout(500); - const createTenantButton = await page.waitForSelector( - 'div[class$=ReactModalPortal] div[class$=dropdownContainer] > div[class$=dropdown] > div[class$=createTenantButton][role=button]:has(div)' - ); - await expect(createTenantButton).toMatchElement('div', { text: 'Create tenant' }); - await createTenantButton.click(); - - // Create tenant with name 'new-tenant' and tag 'production' - await page.waitForTimeout(500); - await page.waitForSelector( - 'div[class$=ReactModalPortal] div[class*=card][class$=medium] input[type=text][name=name]' - ); - await page.waitForSelector( - 'div[class$=ReactModalPortal] div[class*=radioGroup][class$=small] div[class*=radio][class$=small][role=radio] > div[class$=content]:has(input[value=production])' - ); - await expect(page).toFill( - 'div[class$=ReactModalPortal] div[class*=card][class$=medium] input[type=text][name=name]', - createTenantName - ); - await expect(page).toClick( - 'div[class$=ReactModalPortal] div[class*=radioGroup][class$=small] div[class*=radio][class$=small][role=radio] > div[class$=content]:has(input[value=production])' - ); - - // Click create button - await page.waitForTimeout(500); - await expect(page).toClick( - 'div[class$=ReactModalPortal] div[class*=card][class$=medium] div[class$=footer] button[type=submit]' - ); + await page.waitForTimeout(2000); + await createNewTenant(page, createTenantName); expect(new URL(page.url()).pathname.endsWith(`/get-started`)).toBeTruthy(); }); @@ -170,7 +124,7 @@ describe('smoke testing for cloud', () => { it('can sign out of admin console', async () => { const userInfoButton = await page.waitForSelector('div[class$=topbar] > div[class$=container]'); - await userInfoButton.click(); + await userInfoButton?.click(); // Try awaiting for 500ms before clicking sign-out button await page.waitForTimeout(500); @@ -178,7 +132,7 @@ describe('smoke testing for cloud', () => { const signOutButton = await page.waitForSelector( 'div[class$=ReactModalPortal] div[class$=dropdownContainer] div[class$=dropdownItem]:last-child' ); - await signOutButton.click(); + await signOutButton?.click(); await page.waitForNavigation({ waitUntil: 'networkidle0' }); @@ -208,4 +162,78 @@ describe('smoke testing for cloud', () => { expect(page.url().startsWith(logtoCloudUrl.origin)).toBeTruthy(); expect(page.url().endsWith('/onboarding/welcome')).toBeTruthy(); }); + + it('can complete onboarding process with new account', async () => { + await onboardingWelcome(page); + await onboardingUserSurvey(page); + await onboardingSieConfig(page); + await onboardingFinish(page); + + await page.waitForTimeout(1000); + expect(new URL(page.url()).pathname.endsWith('/get-started')).toBeTruthy(); + }); + + it('go to tenant settings and delete current tenant', async () => { + await page.waitForTimeout(2000); + const tenantSettingButton = await page.waitForSelector( + 'div[class$=content] > div[class$=sidebar] a[class$=row][href$=tenant-settings] > div[class$=title]' + ); + await tenantSettingButton?.click(); + + const deleteButton = await page.waitForSelector( + 'div[class$=main] form[class$=container] div[class$=deletionButtonContainer] button[class$=medium][type=button]' + ); + await deleteButton?.click(); + + const textInput = await page.waitForSelector( + 'div[class$=ReactModalPortal] div[class$=container] input[type=text]' + ); + await textInput?.type('My Project'); + + const deleteConfirmButton = await page.waitForSelector( + 'div[class$=ReactModalPortal] div[class$=footer] > button:last-child' + ); + await deleteConfirmButton?.click(); + + await page.waitForTimeout(2000); + const placeholderTitle = await page.waitForSelector( + 'div[class$=pageContainer] div[class$=placeholder]:has(div[class$=title])' + ); + await expect(placeholderTitle).toMatchElement('div[class$=title]', { + text: 'You haven’t created a tenant yet', + }); + }); + + it('can create tenant from landing page', async () => { + const createTenantButton = await page.waitForSelector( + 'div[class$=pageContainer] div[class$=placeholder] button[class$=button][type=button]' + ); + await createTenantButton?.click(); + + await fillAndCreateTenant(page, 'tenant1'); + await page.waitForTimeout(5000); + expect(new URL(page.url()).pathname.endsWith('/get-started')).toBeTruthy(); + }); + + it('can create two more tenant for new account', async () => { + await createNewTenant(page, 'tenant2'); + await page.waitForTimeout(5000); + await createNewTenant(page, 'tenant3'); + await page.waitForTimeout(5000); + expect(new URL(page.url()).pathname.endsWith('/get-started')).toBeTruthy(); + }); + + it('can not open create tenant modal when reach the limit', async () => { + await page.waitForTimeout(2000); + await openTenantDropdown(page); + await openCreateTenantModal(page); + + await page.waitForTimeout(500); + const pageTitle = await page.waitForSelector( + 'div[class$=main] > div[class$=container] > div[class$=header]:has(div[class$=title])' + ); + await expect(pageTitle).toMatchElement('div[class$=title]', { + text: 'Something to explore to help you succeed', + }); + }); }); diff --git a/packages/integration-tests/tsconfig.json b/packages/integration-tests/tsconfig.json index 77c159dfa..18b6083cd 100644 --- a/packages/integration-tests/tsconfig.json +++ b/packages/integration-tests/tsconfig.json @@ -9,7 +9,8 @@ "#src/*": [ "src/*" ] - } + }, + "types": ["jest", "jest-puppeteer"], }, "include": ["src"] }