diff --git a/.github/workflows/dispatch_internal_repo_workflow.yaml b/.github/workflows/dispatch_internal_repo_workflow.yaml index c57da147..b4c60d09 100644 --- a/.github/workflows/dispatch_internal_repo_workflow.yaml +++ b/.github/workflows/dispatch_internal_repo_workflow.yaml @@ -113,6 +113,7 @@ jobs: break fi + echo $in_progress echo "Waiting for workflow to start..." sleep 15 done diff --git a/tests/security/config/playwright.config.ts b/tests/security/config/playwright.config.ts index 9eeaa5ba..0e6a3aa3 100644 --- a/tests/security/config/playwright.config.ts +++ b/tests/security/config/playwright.config.ts @@ -44,6 +44,10 @@ export default defineConfig({ slowMo: 0, }, video: 'on', + viewport: { + height: 1200, + width: 1600, + }, headless: true, }, }, diff --git a/tests/security/functions/common-steps.ts b/tests/security/functions/common-steps.ts index f099f2ed..0566bb06 100644 --- a/tests/security/functions/common-steps.ts +++ b/tests/security/functions/common-steps.ts @@ -79,13 +79,14 @@ export async function createEmailTemplate( '/templates/create-email-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Subject line').fill('E2E subject'); + await page.getByLabel('Subject line').pressSequentially('E2E subject'); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); await page.getByText('Save and preview').click({ position: { @@ -103,12 +104,15 @@ export async function createSmsTemplate( '/templates/create-text-message-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); + await expect(page.getByTestId('character-message-count')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); + await expect(page.getByTestId('character-message-count')).toBeVisible(); await page.getByText('Save and preview').click({ position: { x: 50, @@ -125,12 +129,15 @@ export async function createNhsAppTemplate( '/templates/create-nhs-app-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); + await expect(page.getByTestId('character-count')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); + await expect(page.getByTestId('character-count')).toBeVisible(); await page.getByText('Save and preview').click({ position: { x: 50, @@ -274,7 +281,7 @@ export function copyTemplate( await basePage.clickButtonByName('Save and preview'); await expect(basePage.pageHeader).toHaveText(editedTemplateName); await basePage.clickBackLink(); - await basePage.page.waitForSelector('text=Message templates', { timeout: 10_000 }); + await basePage.page.waitForSelector('text=Message templates'); await basePage.waitForLoad(); await expect(basePage.templateEdited(editedTemplateName)).toBeVisible(); diff --git a/tests/security/functions/login.ts b/tests/security/functions/login.ts index 80b52ed1..de1693f4 100644 --- a/tests/security/functions/login.ts +++ b/tests/security/functions/login.ts @@ -8,67 +8,63 @@ import { getCis2CredentialProvider, } from "nhs-notify-system-tests-shared"; -async function enterCis2TotpCode( +async function enterVerificationCode( page: Page, cis2CredentialProvider: Cis2CredentialProvider, - targetHeadingText: string ) { await page.getByLabel('Enter verification code').fill(cis2CredentialProvider.totp()); - await page.getByText(/\W+Submit\W+/).click(); - const happyPathSelector = page.getByText(targetHeadingText); - const reVerificationSelector = page.locator(`//button[text()=' Re-enter verification code ']`); + await page.getByText('Submit').click(); - await happyPathSelector.or(reVerificationSelector).waitFor({ timeout: 60_000 }); - if (await happyPathSelector.isVisible()) { - return true; + await expect( + page.getByText('Message templates') + .or( + page.getByText('Re-enter verification code') + ) + .or( + page.getByText('Sign in using an NHS account') + )).toBeVisible({ timeout: 90_000 }); + + if (await page.getByText('Message templates').isVisible()) { + return; } - return false; -} -async function enterCis2TotpCodeWithRetry( - page: Page, - cis2CredentialProvider: Cis2CredentialProvider, - targetHeadingText: string -) { - for (var i=0; i<3; i++) { - const success = await enterCis2TotpCode(page, cis2CredentialProvider, targetHeadingText); - if (success) { - return; - } - await page.getByText(/\W+Re-enter verification code\W+/).click(); + if (await page.getByText('Re-enter verification code').isVisible()) { + await page.getByText('Re-enter verification code').click(); + enterVerificationCode(page, cis2CredentialProvider); + + return; + } + + if (await page.getByText('Sign in using an NHS account').isVisible()) { + await cis2Login(page); + + return; } + + throw new Error('Unexpected outcome'); } -async function loginWithCis2( +export async function cis2Login( page: Page, - targetHeadingText: string ) { const cis2CredentialProvider = await getCis2CredentialProvider(); - try { - // Notify WebUI - Click the CIS2 login button - const cis2Button = page.getByText("Log in with my Care Identity") - await page.waitForLoadState('networkidle') - await cis2Button.click(); - - // CIS2 - Select credentials type - await page.getByLabel("Authenticator app").click(); - await page.getByText(/\W+Continue\W+/).click(); - await page.waitForSelector(`//input[@name='password']`); - - // CIS2 - Username/password form - await page.fill('input[name="email"]', cis2CredentialProvider.username); - await page.fill('input[name="password"]', cis2CredentialProvider.password); - await page.getByText(/\W+Continue\W+/).click(); - await expect(page.getByText('Enter verification code')).toBeVisible(); - - // CIS2 - TOTP form - await enterCis2TotpCodeWithRetry(page, cis2CredentialProvider, targetHeadingText); - } catch (error) { - console.error("Error during login:", error); - throw error; - } + await page.waitForLoadState('load'); + + await page.getByText("Log in with my Care Identity").click(); + + await page.getByLabel("Authenticator app").click(); + + await page.getByText('Continue').click(); + + await page.getByLabel('What is your email address?').fill(cis2CredentialProvider.username); + + await page.getByLabel('What is your password?').fill(cis2CredentialProvider.password); + + await page.getByText('Continue').click(); + + await enterVerificationCode(page, cis2CredentialProvider); } async function logOut(page: TemplateMgmtBasePage) { @@ -77,4 +73,4 @@ async function logOut(page: TemplateMgmtBasePage) { await expect(page.pageHeader).toHaveText('Sign in'); } -export { loginWithCis2, expect, logOut }; +export { expect, logOut }; diff --git a/tests/security/tests/routing-config.security.ts b/tests/security/tests/routing-config.security.ts index 30bc01f3..22257828 100644 --- a/tests/security/tests/routing-config.security.ts +++ b/tests/security/tests/routing-config.security.ts @@ -58,6 +58,7 @@ const previewAndSelectTemplate = async ( } test(`User creates a multi-channel routing config`, async ({ page }, { config: { configFile } }) => { + test.setTimeout(180_000); const templates = await getSeededTemplateConfig(configFile); await page.goto('/templates/message-plans'); @@ -111,5 +112,11 @@ test(`User creates a multi-channel routing config`, async ({ page }, { config: { await expect(page).toHaveURL(new RegExp(`/templates/message-plans/review-and-move-to-production/${routingConfigId}(.*)`)); - // remaining pages not ready yet + await page.getByText('Move to production').click(); + + await expect(page).toHaveURL('/templates/message-plans'); + + await page.getByText(/Production \(\d+\)/).click(); + + await expect(page.getByText(routingConfigId)).toBeVisible(); }); diff --git a/tests/security/tests/template-mgmt-cis2-login.security.ts b/tests/security/tests/template-mgmt-cis2-login.security.ts index 06455f61..fff92908 100644 --- a/tests/security/tests/template-mgmt-cis2-login.security.ts +++ b/tests/security/tests/template-mgmt-cis2-login.security.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-regexp */ -import { loginWithCis2, logOut } from '../functions/login'; +import { cis2Login, logOut } from '../functions/login'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { chooseTemplate, @@ -13,7 +13,7 @@ import test from 'playwright/test'; import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; test.use({ storageState: { cookies: [], origins: [] } }); -test.setTimeout(180_000); +test.setTimeout(300_000); /** * Intention is to cover: @@ -40,14 +40,14 @@ test('User logs in via CIS2, saves data in templates, logs out and logs back in const name = 'CIS2 login test'; await startPage({ basePage }); - await loginWithCis2(basePage.page, 'Message templates'); + await cis2Login(basePage.page); await startNewTemplate(props); await chooseTemplate(props, channel); await createEmailTemplate(page, name); await previewPage(props, channelPath, name); await context.storageState({ path: 'login-state/cis2.json' }); await logOut(basePage); - await page.waitForLoadState('networkidle'); + await page.waitForLoadState('load'); await startPage({ basePage }); - await loginWithCis2(basePage.page, 'Message templates'); + await cis2Login(basePage.page); }); diff --git a/tests/test-team/config/dev.config.ts b/tests/test-team/config/dev.config.ts index cba962b0..2e99f0b1 100644 --- a/tests/test-team/config/dev.config.ts +++ b/tests/test-team/config/dev.config.ts @@ -34,6 +34,10 @@ export default defineConfig({ slowMo: 0, }, video: 'on', + viewport: { + height: 1200, + width: 1600, + }, headless: true }, }, diff --git a/tests/test-team/functions/login.ts b/tests/test-team/functions/login.ts index a4397058..b57a476b 100644 --- a/tests/test-team/functions/login.ts +++ b/tests/test-team/functions/login.ts @@ -5,75 +5,63 @@ import { getCis2CredentialProvider, } from 'nhs-notify-system-tests-shared'; -async function enterCis2TotpCode( +async function enterVerificationCode( page: Page, cis2CredentialProvider: Cis2CredentialProvider, - targetHeadingText: string ) { await page.getByLabel('Enter verification code').fill(cis2CredentialProvider.totp()); - await page.getByText(/\W+Submit\W+/).click(); - - const happyPathSelector = page.getByText(targetHeadingText); - const reVerificationSelector = page.locator( - `//button[text()=' Re-enter verification code ']` - ); - - await happyPathSelector - .or(reVerificationSelector) - .waitFor({ timeout: 30_000 }); - if (await happyPathSelector.isVisible()) { - return true; + + await page.getByText('Submit').click(); + + await expect( + page.getByText('Message templates') + .or( + page.getByText('Re-enter verification code') + ) + .or( + page.getByText('Sign in using an NHS account') + )).toBeVisible({ timeout: 90_000 }); + + if (await page.getByText('Message templates').isVisible()) { + return; } - return false; -} -async function enterCis2TotpCodeWithRetry( - page: Page, - cis2CredentialProvider: Cis2CredentialProvider, - targetHeadingText: string -) { - for (var i = 0; i < 3; i++) { - const success = await enterCis2TotpCode( - page, - cis2CredentialProvider, - targetHeadingText - ); - if (success) { - return; - } - await page.getByText(/\W+Re-enter verification code\W+/).click(); + if (await page.getByText('Re-enter verification code').isVisible()) { + await page.getByText('Re-enter verification code').click(); + enterVerificationCode(page, cis2CredentialProvider); + + return; + } + + if (await page.getByText('Sign in using an NHS account').isVisible()) { + await cis2Login(page); + + return; } + + throw new Error('Unexpected outcome'); } -async function loginWithCis2( +export async function cis2Login( page: Page, - targetHeadingText: string ) { const cis2CredentialProvider = await getCis2CredentialProvider(); - try { - // Notify WebUI - Click the CIS2 login button - const cis2Button = page.getByText('Log in with my Care Identity'); - await page.waitForLoadState('networkidle'); - await cis2Button.click(); - - // CIS2 - Select credentials type - await page.getByLabel('Authenticator app').click(); - await page.getByText(/\W+Continue\W+/).click(); - await page.waitForSelector(`//input[@name='password']`); - - // CIS2 - Username/password form - await page.fill('input[name="email"]', cis2CredentialProvider.username); - await page.fill('input[name="password"]', cis2CredentialProvider.password); - await page.getByText(/\W+Continue\W+/).click(); - await expect(page.getByText('Enter verification code')).toBeVisible(); - - // CIS2 - TOTP form - await enterCis2TotpCodeWithRetry(page, cis2CredentialProvider, targetHeadingText); - } catch (error) { - console.error('Error during login:', error); - throw error; - } + await page.waitForLoadState('load'); + + await page.getByText("Log in with my Care Identity").click(); + + await page.getByLabel("Authenticator app").click(); + + await page.getByText('Continue').click(); + + await page.getByLabel('What is your email address?').fill(cis2CredentialProvider.username); + + await page.getByLabel('What is your password?').fill(cis2CredentialProvider.password); + + await page.getByText('Continue').click(); + + await enterVerificationCode(page, cis2CredentialProvider); } async function logOut(page: TemplateMgmtBasePage) { @@ -82,4 +70,4 @@ async function logOut(page: TemplateMgmtBasePage) { await expect(page.pageHeader).toHaveText('Sign in'); } -export { loginWithCis2, expect, logOut }; +export { expect, logOut }; diff --git a/tests/test-team/functions/template-mgmt-e2e-common-steps.ts b/tests/test-team/functions/template-mgmt-e2e-common-steps.ts index 38b1833d..6c42afca 100644 --- a/tests/test-team/functions/template-mgmt-e2e-common-steps.ts +++ b/tests/test-team/functions/template-mgmt-e2e-common-steps.ts @@ -82,13 +82,14 @@ export async function createEmailTemplate( '/templates/create-email-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Subject line').fill('E2E subject'); + await page.getByLabel('Subject line').pressSequentially('E2E subject'); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); await page.getByText('Save and preview').click({ position: { @@ -106,12 +107,16 @@ export async function createSmsTemplate( '/templates/create-text-message-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); + await expect(page.getByTestId('character-message-count')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); + + await expect(page.getByTestId('character-message-count')).toBeVisible(); await page.getByText('Save and preview').click({ position: { x: 50, @@ -128,12 +133,15 @@ export async function createNhsAppTemplate( '/templates/create-nhs-app-template' ); + await page.waitForLoadState('load'); await expect(page.getByTestId('navigation-links')).toBeVisible(); + await expect(page.getByTestId('character-count')).toBeVisible(); - await page.getByLabel('Template name').fill(name); + await page.getByLabel('Template name').pressSequentially(name); - await page.getByLabel('Message').fill('E2E Message'); + await page.getByLabel('Message').pressSequentially('E2E Message'); + await expect(page.getByTestId('character-count')).toBeVisible(); await page.getByText('Save and preview').click({ position: { x: 50, diff --git a/tests/test-team/template-mgmt-e2e-tests/routing-config-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/routing-config-e2e.ts index ec5e2499..607734db 100644 --- a/tests/test-team/template-mgmt-e2e-tests/routing-config-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/routing-config-e2e.ts @@ -58,9 +58,10 @@ const previewAndSelectTemplate = async ( } test(`User creates a multi-channel routing config`, async ({ page, }, { config: { configFile } }) => { + test.setTimeout(120_000); const templates = await getSeededTemplateConfig(configFile); - await page.goto(`/templates/message-plans`); + await page.goto('/templates/message-plans'); await page.getByText('New message plan').click(); @@ -111,5 +112,11 @@ test(`User creates a multi-channel routing config`, async ({ page, }, { config: await expect(page).toHaveURL(new RegExp(`/templates/message-plans/review-and-move-to-production/${routingConfigId}(.*)`)); - // remaining pages not ready yet + await page.getByText('Move to production').click(); + + await expect(page).toHaveURL('/templates/message-plans'); + + await page.getByText(/Production \(\d+\)/).click(); + + await expect(page.getByText(routingConfigId)).toBeVisible(); }); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-cis2-login-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/template-mgmt-cis2-login-e2e.ts index 7a1bd0d5..85b001cb 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-cis2-login-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/template-mgmt-cis2-login-e2e.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-regexp */ -import { loginWithCis2, logOut } from '../functions/login'; +import { cis2Login, logOut } from '../functions/login'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { chooseTemplate, @@ -38,7 +38,7 @@ test('User logs in via CIS2, saves data in templates, logs out and logs back in const name = 'CIS2 login test'; await startPage({ basePage }); - await loginWithCis2(basePage.page, 'Message templates'); + await cis2Login(basePage.page); await startNewTemplate(props); await chooseTemplate(props, channel); await createEmailTemplate(page, name); @@ -47,5 +47,5 @@ test('User logs in via CIS2, saves data in templates, logs out and logs back in await logOut(basePage); await page.waitForLoadState('networkidle'); await startPage({ basePage }); - await loginWithCis2(basePage.page, 'Message templates'); + await cis2Login(basePage.page); });