diff --git a/.github/actions/product-tests/action.yaml b/.github/actions/product-tests/action.yaml index 4a8f761e..1f974cee 100644 --- a/.github/actions/product-tests/action.yaml +++ b/.github/actions/product-tests/action.yaml @@ -35,6 +35,16 @@ runs: gh run download "${{ inputs.github_run_id }}" --repo "${{ inputs.github_repository }}" --name "$ARTIFACT_NAME" --dir "$DEST_PATH" shell: bash + - name: Download setup JSON artifact (templates service) + env: + GH_TOKEN: ${{ inputs.github_token }} + run: | + ARTIFACT_NAME="templates-state" + DEST_PATH="./tests/test-team/lifecycle/templates" + echo "Downloading artifact '$ARTIFACT_NAME' to $DEST_PATH ..." + gh run download "${{ inputs.github_run_id }}" --repo "${{ inputs.github_repository }}" --name "$ARTIFACT_NAME" --dir "$DEST_PATH" + shell: bash + - name: Run product tests shell: bash env: diff --git a/.github/actions/security-scan/action.yaml b/.github/actions/security-scan/action.yaml index 2429c814..54e9e33f 100644 --- a/.github/actions/security-scan/action.yaml +++ b/.github/actions/security-scan/action.yaml @@ -39,6 +39,16 @@ runs: gh run download "${{ inputs.github_run_id }}" --repo "${{ inputs.github_repository }}" --name "$ARTIFACT_NAME" --dir "$DEST_PATH" shell: bash + - name: Download setup JSON artifact (templates service) + env: + GH_TOKEN: ${{ inputs.github_token }} + run: | + ARTIFACT_NAME="templates-state" + DEST_PATH="./tests/security/lifecycle/templates" + echo "Downloading artifact '$ARTIFACT_NAME' to $DEST_PATH ..." + gh run download "${{ inputs.github_run_id }}" --repo "${{ inputs.github_repository }}" --name "$ARTIFACT_NAME" --dir "$DEST_PATH" + shell: bash + - name: Run test - security scan id: test shell: bash diff --git a/tests/security/fixtures/clients.ts b/tests/security/fixtures/clients.ts index a6c34d14..3db5f01d 100644 --- a/tests/security/fixtures/clients.ts +++ b/tests/security/fixtures/clients.ts @@ -45,4 +45,40 @@ export const clients: Record = { name: 'Client 4 Security ZAP Spider Scan', }, }, + PrimaryRoutingEnabledSec: { + templates: { + campaignIds: ['PrimaryRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Primary - Routing Enabled', + }, + }, + CopyRoutingEnabledSec: { + templates: { + campaignIds: ['CopyRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Copy - Routing Enabled', + }, + }, + DeleteRoutingEnabledSec: { + templates: { + campaignIds: ['DeleteRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Delete - Routing Enabled', + }, + }, }; diff --git a/tests/security/fixtures/users.ts b/tests/security/fixtures/users.ts index 0c05858d..c572c4ff 100644 --- a/tests/security/fixtures/users.ts +++ b/tests/security/fixtures/users.ts @@ -18,4 +18,16 @@ export const users: Record = { clientKey: 'SecSpider', clientConfig: clients['SecSpider'].auth, }, + primaryRoutingEnabled: { + clientKey: 'PrimaryRoutingEnabledSec', + clientConfig: clients['PrimaryRoutingEnabledSec'].auth, + }, + copyRoutingEnabled: { + clientKey: 'CopyRoutingEnabledSec', + clientConfig: clients['CopyRoutingEnabledSec'].auth, + }, + deleteRoutingEnabled: { + clientKey: 'DeleteRoutingEnabledSec', + clientConfig: clients['DeleteRoutingEnabledSec'].auth, + }, }; diff --git a/tests/security/functions/common-steps.ts b/tests/security/functions/common-steps.ts index df240f0a..f099f2ed 100644 --- a/tests/security/functions/common-steps.ts +++ b/tests/security/functions/common-steps.ts @@ -1,23 +1,22 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, Page } from '@playwright/test'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; type CommonStepsProps = { basePage: TemplateMgmtBasePage; - baseURL?: string; }; type CommonLetterStepsProps = CommonStepsProps & { letterPage: TemplateMgmtLetterPage; }; -export function startPage({ basePage, baseURL }: CommonStepsProps) { +export function startPage({ basePage }: CommonStepsProps) { return test.step('start page', async () => { await basePage.navigateTo( - `${baseURL}/templates/create-and-submit-templates` + '/templates/create-and-submit-templates' ); await expect(basePage.page).toHaveURL( - `${baseURL}/templates/create-and-submit-templates` + '/templates/create-and-submit-templates' ); await expect(basePage.pageHeader).toHaveText( 'Create and submit a template to NHS Notify' @@ -33,12 +32,12 @@ export function startNewTemplate({ basePage }: CommonStepsProps) { } export function chooseTemplate( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channel: string ) { return test.step('Choose template type', async () => { await expect(basePage.page).toHaveURL( - `${baseURL}/templates/choose-a-template-type` + '/templates/choose-a-template-type' ); await expect(basePage.pageHeader).toHaveText( @@ -52,14 +51,12 @@ export function chooseTemplate( } export function createLetterTemplate( - { basePage, baseURL, letterPage }: CommonLetterStepsProps, + { basePage, letterPage }: CommonLetterStepsProps, name: string ) { return test.step('Create template', async () => { - const pageSlug = 'upload-letter-template'; - await expect(basePage.page).toHaveURL( - `${baseURL}/templates/${pageSlug}` + '/templates/upload-letter-template' ); await expect(basePage.pageHeader).toHaveText( @@ -74,80 +71,113 @@ export function createLetterTemplate( }); } -export function createTemplate( - { basePage, baseURL }: CommonStepsProps, - channel: string, - channelPath: string, - name: string +export async function createEmailTemplate( + page: Page, + name: string, ) { - return test.step('Create template', async () => { + await expect(page).toHaveURL( + '/templates/create-email-template' + ); - const pageSlug = `create-${channelPath}-template` + await expect(page.getByTestId('navigation-links')).toBeVisible(); - await expect(basePage.page).toHaveURL( - `${baseURL}/templates/${pageSlug}` - ); - if (channel === 'Email') { - await expect(basePage.pageHeader).toHaveText(`Create email template`); - } else if (channel === 'Text message (SMS)') { - await expect(basePage.pageHeader).toHaveText( - `Create text message template` - ); - } else { - await expect(basePage.pageHeader).toHaveText( - `Create ${channel} template` - ); + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Subject line').fill('E2E subject'); + + await page.getByLabel('Message').fill('E2E Message'); + + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 } + }); +} - await basePage.fillTextBox('Template name', name); +export async function createSmsTemplate( + page: Page, + name: string, +) { + await expect(page).toHaveURL( + '/templates/create-text-message-template' + ); + + await expect(page.getByTestId('navigation-links')).toBeVisible(); + + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Message').fill('E2E Message'); - if (channel === 'Email') { - await basePage.fillTextBox('Subject line', 'E2E subject'); + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 } + }); +} - await basePage.fillTextBox('Message', 'E2E Message'); - await basePage.clickButtonByName('Save and preview'); +export async function createNhsAppTemplate( + page: Page, + name: string, +) { + await expect(page).toHaveURL( + '/templates/create-nhs-app-template' + ); + + await expect(page.getByTestId('navigation-links')).toBeVisible(); + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Message').fill('E2E Message'); + + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 + } }); } export function requestProof( - { basePage, baseURL, letterPage }: CommonLetterStepsProps, - channel: string, + { basePage, letterPage }: CommonLetterStepsProps, channelPath: string, + routingEnabled: boolean, ) { return test.step('Request Proof', async () => { - const maxRetries = 10; - const retryInterval = 2000; await basePage.clickButtonByName('Request a proof'); await basePage.clickButtonByName('Go back'); await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/preview-${channelPath}-template/(.*)`) + new RegExp(`/templates/preview-${channelPath}-template/(.*)`) ); await basePage.clickButtonByName('Request a proof'); await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/request-proof-of-template/(.*)`) + new RegExp('/templates/request-proof-of-template/(.*)') ); await basePage.clickButtonByName('Request a proof'); await basePage.checkStatus('Waiting for proof'); await letterPage.waitForProofRequest(); await letterPage.verifyFiles(); - await letterPage.submitLetterTemplate(); + if (routingEnabled) { + await letterPage.approveLetterTemplate(); + } else { + await letterPage.submitLetterTemplate(); + } }) } export function previewPage( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, name: string ) { return test.step('Preview page', async () => { await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/preview-${channelPath}-template/(.*)`) + new RegExp(`/templates/preview-${channelPath}-template/(.*)`) ); await expect(basePage.pageHeader).toHaveText(name); @@ -155,7 +185,7 @@ export function previewPage( } export function previewPageChooseSubmit( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, ) { return test.step('Preview page - select submit', async () => { @@ -165,28 +195,26 @@ export function previewPageChooseSubmit( await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/submit-${channelPath}-template/(.*)`) + new RegExp(`/templates/submit-${channelPath}-template/(.*)`) ); }); } export function deleteTemplate( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, name:string ) { return test.step('Delete template', async () => { await basePage.goBackLink.click(); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); const rowCount = await basePage.tableRows(); await basePage.clickLinkByName('Delete ' + name); await basePage.clickButtonByName('No, go back'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); let rowCountCheck = await basePage.tableRows(); expect(rowCount).toBe(rowCount); @@ -194,8 +222,7 @@ export function deleteTemplate( await basePage.clickLinkByName('Delete ' + name); await basePage.clickButtonByName('Yes, delete template'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); rowCountCheck = await basePage.tableRows(); expect(rowCountCheck).toBe(rowCount-1); @@ -204,14 +231,13 @@ export function deleteTemplate( } export function copyTemplate( - { basePage, baseURL }: CommonStepsProps, - name:string + { basePage }: CommonStepsProps, + routingEnabled = false, ) { return test.step('Copy template', async () => { await basePage.goBackLink.click(); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); const rowCount = await basePage.tableRows(); @@ -223,23 +249,25 @@ export function copyTemplate( } await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/copy-template/(.*)`) + new RegExp(`/templates/copy-template/(.*)`) ); await basePage.checkRadio('Email'); await basePage.clickButtonByName('Continue'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); await basePage.page.reload(); // shouldn't need to do this await basePage.clickFirstTableRowLink(); - await basePage.checkRadio('Edit template'); - await basePage.clickButtonByName('Continue'); + if (routingEnabled) { + await basePage.page.getByText('Edit template').click(); + } else { + await basePage.checkRadio('Edit template'); + await basePage.clickButtonByName('Continue'); + } const timestamp = Date.now(); const editedTemplateName = `Test edit changed ${timestamp}`; await basePage.fillTextBox('Template name', editedTemplateName); @@ -256,14 +284,14 @@ export function copyTemplate( } export function submitPage( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, name: string ) { return test.step('Submit page', async () => { await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/submit-${channelPath}-template/(.*)`) + new RegExp(`/templates/submit-${channelPath}-template/(.*)`) ); await expect(basePage.pageHeader).toHaveText('Submit ' + `'` + name + `'`); @@ -273,7 +301,7 @@ export function submitPage( // Submitted Page await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/${channelPath}-template-submitted/(.*)`) + new RegExp(`/templates/${channelPath}-template-submitted/(.*)`) ); await expect(basePage.pageHeader).toHaveText('Template submitted'); diff --git a/tests/security/functions/login.ts b/tests/security/functions/login.ts index da92fada..332d2ccc 100644 --- a/tests/security/functions/login.ts +++ b/tests/security/functions/login.ts @@ -19,7 +19,7 @@ async function enterCis2TotpCode( const happyPathSelector = page.getByText(targetHeadingText); const reVerificationSelector = page.locator(`//button[text()=' Re-enter verification code ']`); - await happyPathSelector.or(reVerificationSelector).waitFor({ timeout: 30_000 }); + await happyPathSelector.or(reVerificationSelector).waitFor({ timeout: 60_000 }); if (await happyPathSelector.isVisible()) { return true; } @@ -41,11 +41,10 @@ async function enterCis2TotpCodeWithRetry( } async function loginWithCis2( - basePage: TemplateMgmtBasePage, + page: Page, targetHeadingText: string ) { const cis2Credentials = await getCis2Credentials(); - const page = basePage.page; try { // Notify WebUI - Click the CIS2 login button diff --git a/tests/security/lifecycle/templates/setup.ts b/tests/security/lifecycle/templates/setup.ts index b7185182..ec91808e 100644 --- a/tests/security/lifecycle/templates/setup.ts +++ b/tests/security/lifecycle/templates/setup.ts @@ -6,6 +6,7 @@ import { getCis2ClientId, TemplateFactory, StorageHelper, + TemplateType, } from 'nhs-notify-system-tests-shared'; import { clients } from '../../fixtures/clients'; import { randomUUID } from 'node:crypto'; @@ -52,8 +53,45 @@ async function main() { clientIds['SecSpider'] ); + const multiChannelRoutingConfigNhsAppTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledSec'], + TemplateType.NHS_APP, + { + name: 'multi-channel-routing-config-nhsapp-template-name', + message: 'multi-channel-routing-config-nhsapp-message', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigNhsApp', multiChannelRoutingConfigNhsAppTemplate); + + const multiChannelRoutingConfigEmailTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledSec'], + TemplateType.EMAIL, + { + name: 'multi-channel-routing-config-email-template-name', + message: 'multi-channel-routing-config-email-template-message', + subject: 'multi-channel-routing-config-email-template-subject', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigEmail', multiChannelRoutingConfigEmailTemplate); + + const multiChannelRoutingConfigSmsTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledSec'], + TemplateType.SMS, + { + name: 'multi-channel-routing-config-sms-template-name', + message: 'multi-channel-routing-config-sms-template-message', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigSms', multiChannelRoutingConfigSmsTemplate); + await new StorageHelper(`nhs-notify-${targetEnvrionment}-app-api-templates`, [ smsTemplate, + multiChannelRoutingConfigNhsAppTemplate, + multiChannelRoutingConfigEmailTemplate, + multiChannelRoutingConfigSmsTemplate, ]).seedData(); await stateFile.persist(); diff --git a/tests/security/lifecycle/templates/teardown.ts b/tests/security/lifecycle/templates/teardown.ts index ed899f01..106e355c 100644 --- a/tests/security/lifecycle/templates/teardown.ts +++ b/tests/security/lifecycle/templates/teardown.ts @@ -3,7 +3,7 @@ import { parseSetupTeardownArgs, StateFile, deleteClientConfigs, - deleteClientTemplates, + deleteClientEntries, } from 'nhs-notify-system-tests-shared'; import z from 'zod'; @@ -48,13 +48,19 @@ async function main() { z.string().default('') ); - const deleted = await Promise.allSettled( + const deletedTemplates = await Promise.allSettled( [...clientIds, cis2ClientId].map((id) => - deleteClientTemplates(targetEnvrionment, id) + deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-templates`) ) ); - const failures = deleted.flatMap((res) => + const deletedRoutingConfigs = await Promise.allSettled( + [...clientIds, cis2ClientId].map((id) => + deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-routing-configuration`) + ) + ); + + const failures = [...deletedTemplates, ...deletedRoutingConfigs].flatMap((res) => res.status === 'rejected' ? [res.reason] : [] ); diff --git a/tests/security/pages/template-mgmt-letter-page.ts b/tests/security/pages/template-mgmt-letter-page.ts index 26cbf696..6d2b071c 100644 --- a/tests/security/pages/template-mgmt-letter-page.ts +++ b/tests/security/pages/template-mgmt-letter-page.ts @@ -132,4 +132,10 @@ export class TemplateMgmtLetterPage extends TemplateMgmtBasePage { await this.page.getByRole('button', { name: 'Approve and submit' }).click(); await expect(this.page.locator('#template-submitted')).toBeVisible(); } + + async approveLetterTemplate() { + await this.page.getByTestId('preview-letter-template-cta').click(); + await this.page.getByText('Approve template proof').click(); + await expect(this.page.getByText('Message templates')).toBeVisible(); + } } diff --git a/tests/security/rules.conf b/tests/security/rules.conf index c790a8ca..5d3bf755 100644 --- a/tests/security/rules.conf +++ b/tests/security/rules.conf @@ -13,7 +13,7 @@ # Already using AJAX spider 10109 IGNORE (Modern Web Application) # Timestamp disclosure - false positives -10096 OUTOFSCOPE .*_next/static.* +10096 IGNORE (Timestamp Disclosure) # Suspicious comments - false positives 10027 IGNORE (Information Disclosure - Suspicious Comments) # Identifies a 'hot-spot' for potential XSS if input is not validated diff --git a/tests/security/tests/routing-config.security.ts b/tests/security/tests/routing-config.security.ts new file mode 100644 index 00000000..30bc01f3 --- /dev/null +++ b/tests/security/tests/routing-config.security.ts @@ -0,0 +1,115 @@ +import path, { dirname } from 'node:path'; +import { expect, Page, test } from '@playwright/test'; +import { StateFile } from 'nhs-notify-system-tests-shared'; +import z, { string } from 'zod'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +const getSeededTemplateConfig = async (configFile: string | undefined) => { + + const projectRoot = path.resolve(dirname(configFile ?? ''), '..'); + + const templatesStateFile = new StateFile( + path.join(projectRoot, 'lifecycle', 'templates'), + process.env.RUN_ID + ); + + await templatesStateFile.loadFromDisk(); + + return templatesStateFile.getValues( + 'templates', + z.record(z.string(), z.object({ + id: z.string(), + name: string(), + message: string(), + })), + ); +} + +const previewAndSelectTemplate = async ( + page: Page, + routingConfigId: string, + channel: string, + channelUrlSegment: string, + { id: templateId, name: templateName, message: templateMessage }: { id: string, name: string, message: string }, +) => { + + await page.getByTestId(`choose-template-link-${channel}`).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}\?(.*)`)); + + await page.getByTestId(`${templateId}-preview-link`).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}/preview-template/${templateId}\?(.*)`)); + + await expect(page.getByText(templateId)).toBeVisible(); + await expect(page.getByText(templateName)).toBeVisible(); + await expect(page.getByText(templateMessage)).toBeVisible(); + + await page.getByTestId('back-link-bottom').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}\?(.*)`)); + + await page.getByTestId(`${templateId}-radio`).check(); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-templates/${routingConfigId}(.*)`)); +} + +test(`User creates a multi-channel routing config`, async ({ page }, { config: { configFile } }) => { + + const templates = await getSeededTemplateConfig(configFile); + await page.goto('/templates/message-plans'); + + await page.getByText('New message plan').click(); + + await expect(page).toHaveURL('/templates/message-plans/choose-message-order'); + + await page.getByLabel('NHS App, Email, Text message', { exact: true }).check(); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL('/templates/message-plans/create-message-plan?messageOrder=NHSAPP%2CEMAIL%2CSMS'); + + await page.getByLabel('Message plan name').fill('message plan name'); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL(new RegExp('/templates/message-plans/choose-templates/(.*)')); + + const urlSegments = page.url().split('/'); + const routingConfigId = urlSegments[urlSegments.length - 1]; + + await previewAndSelectTemplate( + page, + routingConfigId, + 'NHSAPP', + 'nhs-app', + templates['multiChannelRoutingConfigNhsApp'] + ); + await previewAndSelectTemplate( + page, + routingConfigId, + 'EMAIL', + 'email', + templates['multiChannelRoutingConfigEmail'] + ); + await previewAndSelectTemplate( + page, + routingConfigId, + 'SMS', + 'text-message', + templates['multiChannelRoutingConfigSms'] + ); + + await page.getByText('Move to production').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/get-ready-to-move/${routingConfigId}(.*)`)); + + await page.getByText('Continue', { exact: true }).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/review-and-move-to-production/${routingConfigId}(.*)`)); + + // remaining pages not ready yet +}); diff --git a/tests/security/tests/template-mgmt-cis2-login.security.ts b/tests/security/tests/template-mgmt-cis2-login.security.ts index f99bd8f0..06455f61 100644 --- a/tests/security/tests/template-mgmt-cis2-login.security.ts +++ b/tests/security/tests/template-mgmt-cis2-login.security.ts @@ -4,7 +4,7 @@ import { loginWithCis2, logOut } from '../functions/login'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { chooseTemplate, - createTemplate, + createEmailTemplate, previewPage, startNewTemplate, startPage, @@ -26,34 +26,28 @@ test.setTimeout(180_000); * - CIS2 'prompt=login' works to force a re-authentication */ test('User logs in via CIS2, saves data in templates, logs out and logs back in again', async ({ - baseURL, page, context, }) => { - if (!baseURL) { - throw new Error(`Missing baseURL ${baseURL}`); - } - const basePage = new TemplateMgmtBasePage(page); const letterPage = new TemplateMgmtLetterPage(page); const props = { basePage, - baseURL, letterPage, }; const channel = 'Email'; const channelPath = 'email'; - const name = 'E2E Name'; + const name = 'CIS2 login test'; - await startPage({ basePage, baseURL }); - await loginWithCis2(basePage, 'Message templates'); + await startPage({ basePage }); + await loginWithCis2(basePage.page, 'Message templates'); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + 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 startPage({ basePage, baseURL }); - await loginWithCis2(basePage, 'Message templates'); + await startPage({ basePage }); + await loginWithCis2(basePage.page, 'Message templates'); }); diff --git a/tests/security/tests/template-mgmt-copy.security.ts b/tests/security/tests/template-mgmt-copy.security.ts deleted file mode 100644 index 7ee6022b..00000000 --- a/tests/security/tests/template-mgmt-copy.security.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable security/detect-non-literal-regexp */ - -import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; -import { getRandomChannel } from 'nhs-notify-system-tests-shared'; -import { - startPage, - chooseTemplate, - createTemplate, - previewPage, - startNewTemplate, - copyTemplate, -} from '../functions/common-steps'; - -test.use({ storageState: 'login-state/copy.json' }); - -test(`User copies a template`, async ({ page, baseURL }) => { - const props = { - basePage: new TemplateMgmtBasePage(page), - baseURL, - }; - - const randomChannel = getRandomChannel(); - const channel = randomChannel.name; - const channelPath = randomChannel.path; - const name = 'Test edit'; - - await startPage(props); - await startNewTemplate(props); - await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); - await previewPage(props, channelPath, name); - await copyTemplate(props, name); -}); diff --git a/tests/security/tests/templates-with-routing-disabled/template-mgmt-copy.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-copy.security.ts new file mode 100644 index 00000000..054b3af7 --- /dev/null +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-copy.security.ts @@ -0,0 +1,31 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createEmailTemplate, + previewPage, + startNewTemplate, + copyTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/copy.json' }); + +test(`User copies a template`, async ({ page }) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + + const channel = 'Email'; + const channelPath = 'email'; + const name = 'copy template e2e test'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); + await copyTemplate(props); +}); diff --git a/tests/security/tests/template-mgmt-delete.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-delete.security.ts similarity index 72% rename from tests/security/tests/template-mgmt-delete.security.ts rename to tests/security/tests/templates-with-routing-disabled/template-mgmt-delete.security.ts index 20004f9e..14b3393a 100644 --- a/tests/security/tests/template-mgmt-delete.security.ts +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-delete.security.ts @@ -1,34 +1,33 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, + createEmailTemplate, previewPage, startNewTemplate, deleteTemplate, -} from '../functions/common-steps'; +} from '../../functions/common-steps'; test.use({ storageState: 'login-state/delete.json' }); test(`User deletes a template`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; + const channel = 'Email'; const channelPath = 'email'; - const name = 'Test delete' + const name = 'delete template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createEmailTemplate(page, name); await previewPage(props, channelPath, name); await deleteTemplate(props, name); }); diff --git a/tests/security/tests/template-mgmt-email.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-email.security.ts similarity index 75% rename from tests/security/tests/template-mgmt-email.security.ts rename to tests/security/tests/templates-with-routing-disabled/template-mgmt-email.security.ts index 31e0dd46..43b997eb 100644 --- a/tests/security/tests/template-mgmt-email.security.ts +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-email.security.ts @@ -1,35 +1,34 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, + createEmailTemplate, previewPage, submitPage, startNewTemplate, previewPageChooseSubmit, -} from '../functions/common-steps'; +} from '../../functions/common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new email template successfully`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; + const channel = 'Email'; const channelPath = 'email'; - const name = 'E2E Name'; + const name = 'email template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createEmailTemplate(page, name); await previewPage(props, channelPath, name); await previewPageChooseSubmit(props, channelPath); await submitPage(props, channelPath, name); diff --git a/tests/security/tests/template-mgmt-letter.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-letter.security.ts similarity index 71% rename from tests/security/tests/template-mgmt-letter.security.ts rename to tests/security/tests/templates-with-routing-disabled/template-mgmt-letter.security.ts index dbaa11ef..7293dc69 100644 --- a/tests/security/tests/template-mgmt-letter.security.ts +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-letter.security.ts @@ -1,35 +1,33 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; -import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; import { startPage, chooseTemplate, createLetterTemplate, startNewTemplate, requestProof, -} from '../functions/common-steps'; +} from '../../functions/common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new letter template successfully`, async ({ page, - baseURL, }) => { test.setTimeout(240_000); // override just for this test const props = { basePage: new TemplateMgmtBasePage(page), letterPage: new TemplateMgmtLetterPage(page), - baseURL, }; const channel = 'Letter'; const channelPath = 'letter'; - const name = 'E2E Name'; + const name = 'letter template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); await createLetterTemplate(props, name); - await requestProof(props, channel, channelPath); + await requestProof(props, channelPath, false); }); diff --git a/tests/security/tests/templates-with-routing-disabled/template-mgmt-nhsapp.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-nhsapp.security.ts new file mode 100644 index 00000000..6f3cb1b8 --- /dev/null +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-nhsapp.security.ts @@ -0,0 +1,32 @@ +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createNhsAppTemplate, + previewPage, + submitPage, + startNewTemplate, + previewPageChooseSubmit, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/primary.json' }); + +test(`User creates and submits a new nhsapp template successfully`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'NHS App message'; + const channelPath = 'nhs-app'; + const name = 'nhs app template e2e test'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createNhsAppTemplate(page, name); + await previewPage(props, channelPath, name); + await previewPageChooseSubmit(props, channelPath); + await submitPage(props, channelPath, name); +}); diff --git a/tests/security/tests/template-mgmt-sms.security.ts b/tests/security/tests/templates-with-routing-disabled/template-mgmt-sms.security.ts similarity index 76% rename from tests/security/tests/template-mgmt-sms.security.ts rename to tests/security/tests/templates-with-routing-disabled/template-mgmt-sms.security.ts index bb2ef98b..18493dfa 100644 --- a/tests/security/tests/template-mgmt-sms.security.ts +++ b/tests/security/tests/templates-with-routing-disabled/template-mgmt-sms.security.ts @@ -1,35 +1,33 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, + createSmsTemplate, previewPage, submitPage, startNewTemplate, previewPageChooseSubmit, -} from '../functions/common-steps'; +} from '../../functions/common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new sms template successfully`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; const channel = 'Text message (SMS)'; const channelPath = 'text-message'; - const name = 'E2E Name'; + const name = 'SMS template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createSmsTemplate(page, name); await previewPage(props, channelPath, name); await previewPageChooseSubmit(props, channelPath); await submitPage(props, channelPath, name); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-copy.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-copy.security.ts new file mode 100644 index 00000000..abd4249e --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-copy.security.ts @@ -0,0 +1,31 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createEmailTemplate, + previewPage, + startNewTemplate, + copyTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/copyRoutingEnabled.json' }); + +test(`User copies a template`, async ({ page }) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + + const channel = 'Email'; + const channelPath = 'email'; + const name = 'copy template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); + await copyTemplate(props, true); +}); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-delete.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-delete.security.ts new file mode 100644 index 00000000..e1505588 --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-delete.security.ts @@ -0,0 +1,33 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createEmailTemplate, + previewPage, + startNewTemplate, + deleteTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/deleteRoutingEnabled.json' }); + +test(`User deletes a template`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + + const channel = 'Email'; + const channelPath = 'email'; + const name = 'delete template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); + await deleteTemplate(props, name); +}); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-email.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-email.security.ts new file mode 100644 index 00000000..fd8a02af --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-email.security.ts @@ -0,0 +1,30 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createEmailTemplate, + previewPage, + startNewTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new email template successfully`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'Email'; + const channelPath = 'email'; + const name = 'email template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); +}); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-letter.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-letter.security.ts new file mode 100644 index 00000000..6e65e763 --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-letter.security.ts @@ -0,0 +1,33 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; +import { + startPage, + chooseTemplate, + createLetterTemplate, + startNewTemplate, + requestProof, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new letter template successfully`, async ({ + page, +}) => { + test.setTimeout(240_000); // override just for this test + const props = { + basePage: new TemplateMgmtBasePage(page), + letterPage: new TemplateMgmtLetterPage(page), + }; + const channel = 'Letter'; + const channelPath = 'letter'; + const name = 'letter template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createLetterTemplate(props, name); + await requestProof(props, channelPath, true); +}); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-nhsapp.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-nhsapp.security.ts new file mode 100644 index 00000000..5e6afd21 --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-nhsapp.security.ts @@ -0,0 +1,28 @@ +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createNhsAppTemplate, + previewPage, + startNewTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new nhsapp template successfully`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'NHS App message'; + const channelPath = 'nhs-app'; + const name = 'nhs app template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createNhsAppTemplate(page, name); + await previewPage(props, channelPath, name); +}); diff --git a/tests/security/tests/templates-with-routing-enabled/template-mgmt-sms.security.ts b/tests/security/tests/templates-with-routing-enabled/template-mgmt-sms.security.ts new file mode 100644 index 00000000..a863ad02 --- /dev/null +++ b/tests/security/tests/templates-with-routing-enabled/template-mgmt-sms.security.ts @@ -0,0 +1,30 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + createSmsTemplate, + previewPage, + startNewTemplate, +} from '../../functions/common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new sms template successfully`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'Text message (SMS)'; + const channelPath = 'text-message'; + const name = 'SMS template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createSmsTemplate(page, name); + await previewPage(props, channelPath, name); +}); diff --git a/tests/shared/setup-teardown/domain/delete-client-templates.ts b/tests/shared/setup-teardown/domain/delete-client-templates.ts index 3e055200..a47d8987 100644 --- a/tests/shared/setup-teardown/domain/delete-client-templates.ts +++ b/tests/shared/setup-teardown/domain/delete-client-templates.ts @@ -8,12 +8,10 @@ import { const client = new DynamoDBClient({ region: 'eu-west-2' }); -export async function deleteClientTemplates( - environment: string, - clientId: string +export async function deleteClientEntries( + clientId: string, + tableName: string, ): Promise { - const tableName = `nhs-notify-${environment}-app-api-templates`; - const input: QueryCommandInput = { TableName: tableName, KeyConditionExpression: '#owner = :owner', diff --git a/tests/test-team/fixtures/clients.ts b/tests/test-team/fixtures/clients.ts index 98cf4de9..fcbc5242 100644 --- a/tests/test-team/fixtures/clients.ts +++ b/tests/test-team/fixtures/clients.ts @@ -6,6 +6,7 @@ export const clients: Record = { campaignIds: ['ProductPrimary-Campaign'], features: { proofing: true, + routing: false, }, }, auth: { @@ -17,6 +18,7 @@ export const clients: Record = { campaignIds: ['Client2Product-Campaign'], features: { proofing: true, + routing: false, }, }, auth: { @@ -28,10 +30,47 @@ export const clients: Record = { campaignIds: ['Client3Product-Campaign'], features: { proofing: true, + routing: false, }, }, auth: { name: 'Client 3 Product', }, }, + PrimaryRoutingEnabledProduct: { + templates: { + campaignIds: ['PrimaryRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Primary - Routing Enabled', + }, + }, + CopyRoutingEnabledProduct: { + templates: { + campaignIds: ['CopyRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Copy - Routing Enabled', + }, + }, + DeleteRoutingEnabledProduct: { + templates: { + campaignIds: ['DeleteRoutingEnabled-Campaign'], + features: { + proofing: true, + routing: true, + }, + }, + auth: { + name: 'Delete - Routing Enabled', + }, + }, }; diff --git a/tests/test-team/fixtures/users.ts b/tests/test-team/fixtures/users.ts index 964872bd..51a5cdcf 100644 --- a/tests/test-team/fixtures/users.ts +++ b/tests/test-team/fixtures/users.ts @@ -14,4 +14,16 @@ export const users: Record = { clientKey: 'Client3Product', clientConfig: clients['Client3Product'].auth, }, + primaryRoutingEnabled: { + clientKey: 'PrimaryRoutingEnabledProduct', + clientConfig: clients['PrimaryRoutingEnabledProduct'].auth, + }, + copyRoutingEnabled: { + clientKey: 'CopyRoutingEnabledProduct', + clientConfig: clients['CopyRoutingEnabledProduct'].auth, + }, + deleteRoutingEnabled: { + clientKey: 'DeleteRoutingEnabledProduct', + clientConfig: clients['DeleteRoutingEnabledProduct'].auth, + }, }; diff --git a/tests/test-team/functions/login.ts b/tests/test-team/functions/login.ts index ed8bef64..4f3d27cf 100644 --- a/tests/test-team/functions/login.ts +++ b/tests/test-team/functions/login.ts @@ -46,11 +46,10 @@ async function enterCis2TotpCodeWithRetry( } async function loginWithCis2( - basePage: TemplateMgmtBasePage, + page: Page, targetHeadingText: string ) { const cis2Credentials = await getCis2Credentials(); - const page = basePage.page; try { // Notify WebUI - Click the CIS2 login button 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 d3e2b6b2..38b1833d 100644 --- a/tests/test-team/functions/template-mgmt-e2e-common-steps.ts +++ b/tests/test-team/functions/template-mgmt-e2e-common-steps.ts @@ -1,23 +1,22 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, Page } from '@playwright/test'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; type CommonStepsProps = { basePage: TemplateMgmtBasePage; - baseURL?: string; }; type CommonLetterStepsProps = CommonStepsProps & { letterPage: TemplateMgmtLetterPage; }; -export function startPage({ basePage, baseURL }: CommonStepsProps) { +export function startPage({ basePage }: CommonStepsProps) { return test.step('start page', async () => { await basePage.navigateTo( - `${baseURL}/templates/create-and-submit-templates` + '/templates/create-and-submit-templates' ); await expect(basePage.page).toHaveURL( - `${baseURL}/templates/create-and-submit-templates` + '/templates/create-and-submit-templates' ); await expect(basePage.pageHeader).toHaveText( 'Create and submit a template to NHS Notify' @@ -33,12 +32,12 @@ export function startNewTemplate({ basePage }: CommonStepsProps) { } export function chooseTemplate( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channel: string ) { return test.step('Choose template type', async () => { await expect(basePage.page).toHaveURL( - `${baseURL}/templates/choose-a-template-type` + '/templates/choose-a-template-type' ); await expect(basePage.pageHeader).toHaveText( @@ -52,17 +51,15 @@ export function chooseTemplate( } export function createLetterTemplate( - { basePage, baseURL, letterPage }: CommonLetterStepsProps, + { basePage, letterPage }: CommonLetterStepsProps, name: string, language: string, inputFileName: string ) { return test.step('Create template', async () => { - const pageSlug = 'upload-letter-template'; - await expect(basePage.page).toHaveURL( - `${baseURL}/templates/${pageSlug}` + '/templates/upload-letter-template' ); await expect(basePage.pageHeader).toHaveText( @@ -77,80 +74,114 @@ export function createLetterTemplate( }); } -export function createTemplate( - { basePage, baseURL }: CommonStepsProps, - channel: string, - channelPath: string, - name: string +export async function createEmailTemplate( + page: Page, + name: string, ) { - return test.step('Create template', async () => { + await expect(page).toHaveURL( + '/templates/create-email-template' + ); - const pageSlug = `create-${channelPath}-template` + await expect(page.getByTestId('navigation-links')).toBeVisible(); - await expect(basePage.page).toHaveURL( - `${baseURL}/templates/${pageSlug}` - ); - if (channel === 'Email') { - await expect(basePage.pageHeader).toHaveText(`Create email template`); - } else if (channel === 'Text message (SMS)') { - await expect(basePage.pageHeader).toHaveText( - `Create text message template` - ); - } else { - await expect(basePage.pageHeader).toHaveText( - `Create ${channel} template` - ); + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Subject line').fill('E2E subject'); + + await page.getByLabel('Message').fill('E2E Message'); + + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 } + }); +} - await basePage.fillTextBox('Template name', name); +export async function createSmsTemplate( + page: Page, + name: string, +) { + await expect(page).toHaveURL( + '/templates/create-text-message-template' + ); + + await expect(page.getByTestId('navigation-links')).toBeVisible(); + + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Message').fill('E2E Message'); - if (channel === 'Email') { - await basePage.fillTextBox('Subject line', 'E2E subject'); + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 } + }); +} - await basePage.fillTextBox('Message', 'E2E Message'); - await basePage.clickButtonByName('Save and preview'); +export async function createNhsAppTemplate( + page: Page, + name: string, +) { + await expect(page).toHaveURL( + '/templates/create-nhs-app-template' + ); + await expect(page.getByTestId('navigation-links')).toBeVisible(); + + await page.getByLabel('Template name').fill(name); + + await page.getByLabel('Message').fill('E2E Message'); + + await page.getByText('Save and preview').click({ + position: { + x: 50, + y: 50 + } }); } + export function requestProof( - { basePage, baseURL, letterPage }: CommonLetterStepsProps, - channel: string, + { basePage, letterPage }: CommonLetterStepsProps, channelPath: string, + routingEnabled: boolean, ) { return test.step('Request Proof', async () => { - const maxRetries = 10; - const retryInterval = 2000; await basePage.clickButtonByName('Request a proof'); await basePage.clickButtonByName('Go back'); await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/preview-${channelPath}-template/(.*)`) + new RegExp(`/templates/preview-${channelPath}-template/(.*)`) ); await basePage.clickButtonByName('Request a proof'); await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/request-proof-of-template/(.*)`) + new RegExp('/templates/request-proof-of-template/(.*)') ); await basePage.clickButtonByName('Request a proof'); await basePage.checkStatus('Waiting for proof'); await letterPage.waitForProofRequest(); await letterPage.verifyFiles(); - await letterPage.submitLetterTemplate(); -}) + if (routingEnabled) { + await letterPage.approveLetterTemplate(); + } else { + await letterPage.submitLetterTemplate(); + } + }) } export function previewPage( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, name: string ) { return test.step('Preview page', async () => { await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/preview-${channelPath}-template/(.*)`) + new RegExp(`/templates/preview-${channelPath}-template/(.*)`) ); await expect(basePage.pageHeader).toHaveText(name); @@ -158,7 +189,7 @@ export function previewPage( } export function previewPageChooseSubmit( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, ) { return test.step('Preview page - select submit', async () => { @@ -168,20 +199,19 @@ export function previewPageChooseSubmit( await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/submit-${channelPath}-template/(.*)`) + new RegExp(`/templates/submit-${channelPath}-template/(.*)`) ); }); } export function deleteTemplate( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, name:string ) { return test.step('Delete template', async () => { await basePage.goBackLink.click(); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); const rowCount = await basePage.tableRows(); console.log(rowCount); @@ -189,8 +219,7 @@ export function deleteTemplate( await basePage.clickLinkByName('Delete ' + name); await basePage.clickButtonByName('No, go back'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); let rowCountCheck = await basePage.tableRows(); console.log(rowCountCheck); @@ -199,8 +228,7 @@ export function deleteTemplate( await basePage.clickLinkByName('Delete ' + name); await basePage.clickButtonByName('Yes, delete template'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); rowCountCheck = await basePage.tableRows(); console.log(rowCount-1); @@ -210,14 +238,13 @@ export function deleteTemplate( } export function copyTemplate( - { basePage, baseURL }: CommonStepsProps, - name:string + { basePage }: CommonStepsProps, + routingEnabled = false, ) { return test.step('Copy template', async () => { await basePage.goBackLink.click(); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); const rowCount = await basePage.tableRows(); console.log(rowCount); @@ -230,23 +257,25 @@ export function copyTemplate( } await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/copy-template/(.*)`) + new RegExp('/templates/copy-template/(.*)') ); await basePage.checkRadio('Email'); await basePage.clickButtonByName('Continue'); await expect(basePage.page).toHaveURL( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/message-templates`) + '/templates/message-templates' ); await basePage.page.reload(); // shouldn't need to do this await basePage.clickFirstTableRowLink(); - await basePage.checkRadio('Edit template'); - await basePage.clickButtonByName('Continue'); + if (routingEnabled) { + await basePage.page.getByText('Edit template').click(); + } else { + await basePage.checkRadio('Edit template'); + await basePage.clickButtonByName('Continue'); + } const timestamp = Date.now(); const editedTemplateName = `Test edit changed ${timestamp}`; await basePage.fillTextBox('Template name', editedTemplateName); @@ -264,14 +293,14 @@ export function copyTemplate( } export function submitPage( - { basePage, baseURL }: CommonStepsProps, + { basePage }: CommonStepsProps, channelPath: string, name: string ) { return test.step('Submit page', async () => { await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/submit-${channelPath}-template/(.*)`) + new RegExp(`/templates/submit-${channelPath}-template/(.*)`) ); await expect(basePage.pageHeader).toHaveText('Submit ' + `'` + name + `'`); @@ -281,7 +310,7 @@ export function submitPage( // Submitted Page await expect(basePage.page).toHaveURL( // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`${baseURL}/templates/${channelPath}-template-submitted/(.*)`) + new RegExp(`/templates/${channelPath}-template-submitted/(.*)`) ); await expect(basePage.pageHeader).toHaveText('Template submitted'); diff --git a/tests/test-team/lifecycle/templates/setup.ts b/tests/test-team/lifecycle/templates/setup.ts index 7cc14486..70c0f2cf 100644 --- a/tests/test-team/lifecycle/templates/setup.ts +++ b/tests/test-team/lifecycle/templates/setup.ts @@ -1,3 +1,4 @@ +import { randomUUID } from 'node:crypto'; import { createClientConfig, increaseSftpPollingFrequency, @@ -5,6 +6,9 @@ import { parseSetupTeardownArgs, StateFile, getCis2ClientId, + TemplateFactory, + StorageHelper, + TemplateType, } from 'nhs-notify-system-tests-shared'; import { clients } from '../../fixtures/clients'; @@ -35,15 +39,57 @@ async function main() { ) ); + const clientIds = Object.fromEntries(clientEntries.map(([key, { id }]) => [key, id])); + stateFile.setValues( 'clientIds', - Object.fromEntries(clientEntries.map(([key, { id }]) => [key, id])) + clientIds, ); const cis2ClientId = await getCis2ClientId(); stateFile.setValue('cis2', 'notify-client-id', cis2ClientId); + const multiChannelRoutingConfigNhsAppTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledProduct'], + TemplateType.NHS_APP, + { + name: 'multi-channel-routing-config-nhsapp-template-name', + message: 'multi-channel-routing-config-nhsapp-message', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigNhsApp', multiChannelRoutingConfigNhsAppTemplate); + + const multiChannelRoutingConfigEmailTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledProduct'], + TemplateType.EMAIL, + { + name: 'multi-channel-routing-config-email-template-name', + message: 'multi-channel-routing-config-email-template-message', + subject: 'multi-channel-routing-config-email-template-subject', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigEmail', multiChannelRoutingConfigEmailTemplate); + + const multiChannelRoutingConfigSmsTemplate = TemplateFactory.create( + randomUUID(), + clientIds['PrimaryRoutingEnabledProduct'], + TemplateType.SMS, + { + name: 'multi-channel-routing-config-sms-template-name', + message: 'multi-channel-routing-config-sms-template-message', + } + ); + stateFile.setValue('templates', 'multiChannelRoutingConfigSms', multiChannelRoutingConfigSmsTemplate); + + await new StorageHelper(`nhs-notify-${targetEnvrionment}-app-api-templates`, [ + multiChannelRoutingConfigNhsAppTemplate, + multiChannelRoutingConfigEmailTemplate, + multiChannelRoutingConfigSmsTemplate, + ]).seedData(); + await stateFile.persist(); } diff --git a/tests/test-team/lifecycle/templates/teardown.ts b/tests/test-team/lifecycle/templates/teardown.ts index ed899f01..106e355c 100644 --- a/tests/test-team/lifecycle/templates/teardown.ts +++ b/tests/test-team/lifecycle/templates/teardown.ts @@ -3,7 +3,7 @@ import { parseSetupTeardownArgs, StateFile, deleteClientConfigs, - deleteClientTemplates, + deleteClientEntries, } from 'nhs-notify-system-tests-shared'; import z from 'zod'; @@ -48,13 +48,19 @@ async function main() { z.string().default('') ); - const deleted = await Promise.allSettled( + const deletedTemplates = await Promise.allSettled( [...clientIds, cis2ClientId].map((id) => - deleteClientTemplates(targetEnvrionment, id) + deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-templates`) ) ); - const failures = deleted.flatMap((res) => + const deletedRoutingConfigs = await Promise.allSettled( + [...clientIds, cis2ClientId].map((id) => + deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-routing-configuration`) + ) + ); + + const failures = [...deletedTemplates, ...deletedRoutingConfigs].flatMap((res) => res.status === 'rejected' ? [res.reason] : [] ); diff --git a/tests/test-team/pages/template-mgmt-base-page.ts b/tests/test-team/pages/template-mgmt-base-page.ts index ae629b52..8ba0e9cc 100644 --- a/tests/test-team/pages/template-mgmt-base-page.ts +++ b/tests/test-team/pages/template-mgmt-base-page.ts @@ -108,9 +108,9 @@ export class TemplateMgmtBasePage { await expect(this.page.getByText(status)).toBeVisible(); } - async fillTextBox(textBoxName: string, textBoxContent: string) { + async fillTextBox(textBoxLabel: string, textBoxContent: string) { await this.page - .getByRole("textbox", { name: textBoxName }) + .getByLabel(textBoxLabel) .fill(textBoxContent); } diff --git a/tests/test-team/pages/template-mgmt-letter-page.ts b/tests/test-team/pages/template-mgmt-letter-page.ts index 28a6c477..afd7122d 100644 --- a/tests/test-team/pages/template-mgmt-letter-page.ts +++ b/tests/test-team/pages/template-mgmt-letter-page.ts @@ -151,4 +151,10 @@ export class TemplateMgmtLetterPage extends TemplateMgmtBasePage { await this.page.getByRole('button', { name: 'Approve and submit' }).click(); await expect(this.page.locator('#template-submitted')).toBeVisible(); } + + async approveLetterTemplate() { + await this.page.getByTestId('preview-letter-template-cta').click(); + await this.page.getByText('Approve template proof').click(); + await expect(this.page.getByText('Message templates')).toBeVisible(); + } } 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 new file mode 100644 index 00000000..ec5e2499 --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/routing-config-e2e.ts @@ -0,0 +1,115 @@ +import path, { dirname } from 'node:path'; +import { expect, Page, test } from '@playwright/test'; +import { StateFile } from 'nhs-notify-system-tests-shared'; +import z, { string } from 'zod'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +const getSeededTemplateConfig = async (configFile: string | undefined) => { + + const projectRoot = path.resolve(dirname(configFile ?? ''), '..'); + + const templatesStateFile = new StateFile( + path.join(projectRoot, 'lifecycle', 'templates'), + process.env.RUN_ID + ); + + await templatesStateFile.loadFromDisk(); + + return templatesStateFile.getValues( + 'templates', + z.record(z.string(), z.object({ + id: z.string(), + name: string(), + message: string(), + })), + ); +} + +const previewAndSelectTemplate = async ( + page: Page, + routingConfigId: string, + channel: string, + channelUrlSegment: string, + { id: templateId, name: templateName, message: templateMessage }: { id: string, name: string, message: string }, +) => { + + await page.getByTestId(`choose-template-link-${channel}`).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}\?(.*)`)); + + await page.getByTestId(`${templateId}-preview-link`).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}/preview-template/${templateId}\?(.*)`)); + + await expect(page.getByText(templateId)).toBeVisible(); + await expect(page.getByText(templateName)).toBeVisible(); + await expect(page.getByText(templateMessage)).toBeVisible(); + + await page.getByTestId('back-link-bottom').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-${channelUrlSegment}-template/${routingConfigId}\?(.*)`)); + + await page.getByTestId(`${templateId}-radio`).check(); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/choose-templates/${routingConfigId}(.*)`)); +} + +test(`User creates a multi-channel routing config`, async ({ page, }, { config: { configFile } }) => { + + const templates = await getSeededTemplateConfig(configFile); + await page.goto(`/templates/message-plans`); + + await page.getByText('New message plan').click(); + + await expect(page).toHaveURL('/templates/message-plans/choose-message-order'); + + await page.getByLabel('NHS App, Email, Text message', { exact: true }).check(); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL('/templates/message-plans/create-message-plan?messageOrder=NHSAPP%2CEMAIL%2CSMS'); + + await page.getByLabel('Message plan name').fill('message plan name'); + + await page.getByText('Save and continue').click(); + + await expect(page).toHaveURL(new RegExp('/templates/message-plans/choose-templates/(.*)')); + + const urlSegments = page.url().split('/'); + const routingConfigId = urlSegments[urlSegments.length - 1]; + + await previewAndSelectTemplate( + page, + routingConfigId, + 'NHSAPP', + 'nhs-app', + templates['multiChannelRoutingConfigNhsApp'] + ); + await previewAndSelectTemplate( + page, + routingConfigId, + 'EMAIL', + 'email', + templates['multiChannelRoutingConfigEmail'] + ); + await previewAndSelectTemplate( + page, + routingConfigId, + 'SMS', + 'text-message', + templates['multiChannelRoutingConfigSms'] + ); + + await page.getByText('Move to production').click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/get-ready-to-move/${routingConfigId}(.*)`)); + + await page.getByText('Continue', { exact: true }).click(); + + await expect(page).toHaveURL(new RegExp(`/templates/message-plans/review-and-move-to-production/${routingConfigId}(.*)`)); + + // remaining pages not ready yet +}); 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 9facdf89..7a1bd0d5 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 @@ -4,7 +4,7 @@ import { loginWithCis2, logOut } from '../functions/login'; import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; import { chooseTemplate, - createTemplate, + createEmailTemplate, previewPage, startNewTemplate, startPage, @@ -24,32 +24,28 @@ test.use({ storageState: { cookies: [], origins: [] } }); * - CIS2 'prompt=login' works to force a re-authentication */ test('User logs in via CIS2, saves data in templates, logs out and logs back in again', async ({ - baseURL, page, context, }) => { - if (!baseURL) { - throw new Error(`Missing baseURL ${baseURL}`); - } + test.setTimeout(120_000); const basePage = new TemplateMgmtBasePage(page); const props = { basePage, - baseURL, }; const channel = 'Email'; const channelPath = 'email'; - const name = 'E2E Name'; + const name = 'CIS2 login test'; - await startPage({ basePage, baseURL }); - await loginWithCis2(basePage, 'Message templates'); + await startPage({ basePage }); + await loginWithCis2(basePage.page, 'Message templates'); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + 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 startPage({ basePage, baseURL }); - await loginWithCis2(basePage, 'Message templates'); + await startPage({ basePage }); + await loginWithCis2(basePage.page, 'Message templates'); }); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-nhsapp-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/template-mgmt-nhsapp-e2e.ts deleted file mode 100644 index e9ab8990..00000000 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-nhsapp-e2e.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; -import { - startPage, - chooseTemplate, - createTemplate, - previewPage, - submitPage, - startNewTemplate, - previewPageChooseSubmit, -} from '../functions/template-mgmt-e2e-common-steps'; - -test.use({ storageState: 'login-state/primary.json' }); - -test(`User creates and submits a new nhsapp template successfully`, async ({ - page, - baseURL, -}) => { - const props = { - basePage: new TemplateMgmtBasePage(page), - baseURL, - }; - const channel = 'NHS App message'; - const channelPath = 'nhs-app'; - const name = 'E2E Name'; - - await startPage(props); - await startNewTemplate(props); - await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); - await previewPage(props, channelPath, name); - await previewPageChooseSubmit(props, channelPath); - await submitPage(props, channelPath, name); -}); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-copy-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-copy-e2e.ts similarity index 55% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-copy-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-copy-e2e.ts index 370bfa49..3057de13 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-copy-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-copy-e2e.ts @@ -1,36 +1,34 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { getRandomChannel } from 'nhs-notify-system-tests-shared'; import { startPage, chooseTemplate, - createTemplate, previewPage, startNewTemplate, copyTemplate, -} from '../functions/template-mgmt-e2e-common-steps'; + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/copy.json' }); -test(`User copies a template`, async ({ page, baseURL }) => { +test(`User copies a template`, async ({ page }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; - const randomChannel = getRandomChannel(); - const channel = randomChannel.name; - const channelPath = randomChannel.path; - const name = 'Test edit'; + const channel = 'Email'; + const channelPath = 'email'; + const name = 'copy template e2e test'; console.log('name = ', channel, 'path = ', channelPath); await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createEmailTemplate(page, name); await previewPage(props, channelPath, name); - await copyTemplate(props, name); + await copyTemplate(props); }); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-delete-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-delete-e2e.ts similarity index 70% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-delete-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-delete-e2e.ts index 91d2740e..c3ce6da4 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-delete-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-delete-e2e.ts @@ -1,34 +1,32 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, previewPage, startNewTemplate, deleteTemplate, -} from '../functions/template-mgmt-e2e-common-steps'; + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/delete.json' }); test(`User deletes a template`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; const channel = 'Email'; const channelPath = 'email'; - const name = 'Test delete' + const name = 'delete template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createEmailTemplate(page, name); await previewPage(props, channelPath, name); await deleteTemplate(props, name); }); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-email-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-email-e2e.ts similarity index 74% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-email-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-email-e2e.ts index a33a5f0e..25a71616 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-email-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-email-e2e.ts @@ -1,35 +1,34 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, previewPage, submitPage, startNewTemplate, previewPageChooseSubmit, -} from '../functions/template-mgmt-e2e-common-steps'; + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new email template successfully`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; + const channel = 'Email'; const channelPath = 'email'; - const name = 'E2E Name'; + const name = 'email template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createEmailTemplate(page, name); await previewPage(props, channelPath, name); await previewPageChooseSubmit(props, channelPath); await submitPage(props, channelPath, name); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-foreign-letter-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-foreign-letter-e2e.ts similarity index 78% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-foreign-letter-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-foreign-letter-e2e.ts index 7ea3c2dd..818f63a1 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-foreign-letter-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-foreign-letter-e2e.ts @@ -1,14 +1,14 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; -import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; import { startPage, chooseTemplate, createLetterTemplate, startNewTemplate, -} from '../functions/template-mgmt-e2e-common-steps'; +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/primary.json' }); @@ -27,18 +27,16 @@ const testConfigs = [ for (const { language, inputFileName } of testConfigs) { test(`User creates and submits a new letter template successfully - ${language})`, async ({ page, - baseURL, }) => { test.setTimeout(240_000); // override just for this test const props = { basePage: new TemplateMgmtBasePage(page), letterPage: new TemplateMgmtLetterPage(page), - baseURL, }; const channel = 'Letter'; - const name = `E2E Name ${language}`; + const name = `${language} language template e2e test`; await startPage(props); await startNewTemplate(props); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-letter-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-letter-e2e.ts similarity index 72% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-letter-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-letter-e2e.ts index b7e52e28..529b6de0 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-letter-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-letter-e2e.ts @@ -1,31 +1,29 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; -import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; import { startPage, chooseTemplate, createLetterTemplate, startNewTemplate, requestProof, -} from '../functions/template-mgmt-e2e-common-steps'; +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new letter template successfully`, async ({ page, - baseURL, }) => { test.setTimeout(240_000); // override just for this test const props = { basePage: new TemplateMgmtBasePage(page), letterPage: new TemplateMgmtLetterPage(page), - baseURL, }; const channel = 'Letter'; const channelPath = 'letter'; - const name = 'E2E Name'; + const name = 'letter template e2e test'; const language = 'en'; const inputFileName = 'template.pdf'; @@ -33,5 +31,5 @@ test(`User creates and submits a new letter template successfully`, async ({ await startNewTemplate(props); await chooseTemplate(props, channel); await createLetterTemplate(props, name, language, inputFileName); - await requestProof(props, channel, channelPath); + await requestProof(props, channelPath, false); }); diff --git a/tests/security/tests/template-mgmt-nhsapp.security.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-nhsapp-e2e.ts similarity index 74% rename from tests/security/tests/template-mgmt-nhsapp.security.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-nhsapp-e2e.ts index 753337e9..9e9daa29 100644 --- a/tests/security/tests/template-mgmt-nhsapp.security.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-nhsapp-e2e.ts @@ -1,33 +1,31 @@ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, previewPage, submitPage, startNewTemplate, previewPageChooseSubmit, -} from '../functions/common-steps'; + createNhsAppTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new nhsapp template successfully`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; const channel = 'NHS App message'; const channelPath = 'nhs-app'; - const name = 'E2E Name'; + const name = 'nhs app template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createNhsAppTemplate(page, name); await previewPage(props, channelPath, name); await previewPageChooseSubmit(props, channelPath); await submitPage(props, channelPath, name); diff --git a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-sms-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-sms-e2e.ts similarity index 74% rename from tests/test-team/template-mgmt-e2e-tests/template-mgmt-sms-e2e.ts rename to tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-sms-e2e.ts index 7703c7e3..632ba745 100644 --- a/tests/test-team/template-mgmt-e2e-tests/template-mgmt-sms-e2e.ts +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-disabled/template-mgmt-sms-e2e.ts @@ -1,35 +1,33 @@ /* eslint-disable security/detect-non-literal-regexp */ import { test } from '@playwright/test'; -import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; import { startPage, chooseTemplate, - createTemplate, previewPage, submitPage, startNewTemplate, previewPageChooseSubmit, -} from '../functions/template-mgmt-e2e-common-steps'; + createSmsTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; test.use({ storageState: 'login-state/primary.json' }); test(`User creates and submits a new sms template successfully`, async ({ page, - baseURL, }) => { const props = { basePage: new TemplateMgmtBasePage(page), - baseURL, }; const channel = 'Text message (SMS)'; const channelPath = 'text-message'; - const name = 'E2E Name'; + const name = 'SMS template e2e test'; await startPage(props); await startNewTemplate(props); await chooseTemplate(props, channel); - await createTemplate(props, channel, channelPath, name); + await createSmsTemplate(page, name); await previewPage(props, channelPath, name); await previewPageChooseSubmit(props, channelPath); await submitPage(props, channelPath, name); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-copy-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-copy-e2e.ts new file mode 100644 index 00000000..ef8f1d0b --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-copy-e2e.ts @@ -0,0 +1,33 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + previewPage, + startNewTemplate, + copyTemplate, + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/copyRoutingEnabled.json' }); + +test(`User copies a template - routing enabled`, async ({ page }) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + + const channel = 'Email'; + const channelPath = 'email'; + const name = 'copy template e2e test - routing enabled'; + + console.log('name = ', channel, 'path = ', channelPath); + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); + await copyTemplate(props, true); +}); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-delete-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-delete-e2e.ts new file mode 100644 index 00000000..ebac7b31 --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-delete-e2e.ts @@ -0,0 +1,33 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + previewPage, + startNewTemplate, + deleteTemplate, + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/deleteRoutingEnabled.json' }); + +test(`User deletes a template - routing enabled`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + + const channel = 'Email'; + const channelPath = 'email'; + const name = 'delete template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); + await deleteTemplate(props, name); +}); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-email-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-email-e2e.ts new file mode 100644 index 00000000..7b0ab703 --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-email-e2e.ts @@ -0,0 +1,30 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + previewPage, + startNewTemplate, + createEmailTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new email template successfully - routing enabled`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'Email'; + const channelPath = 'email'; + const name = 'email template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createEmailTemplate(page, name); + await previewPage(props, channelPath, name); +}); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-foreign-letter-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-foreign-letter-e2e.ts new file mode 100644 index 00000000..a546a770 --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-foreign-letter-e2e.ts @@ -0,0 +1,46 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; +import { + startPage, + chooseTemplate, + createLetterTemplate, + startNewTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +const testConfigs = [ + { + language: 'bn', + inputFileName: 'Bengali.pdf', + }, + { language: 'ar', inputFileName: 'Arabic.pdf' }, + { + language: 'tr', + inputFileName: 'Turkish.pdf', + }, +]; + +for (const { language, inputFileName } of testConfigs) { + test(`User creates and submits a new letter template successfully - ${language} - routing enabled)`, async ({ + page, + }) => { + test.setTimeout(240_000); // override just for this test + + const props = { + basePage: new TemplateMgmtBasePage(page), + letterPage: new TemplateMgmtLetterPage(page), + }; + + const channel = 'Letter'; + const name = `${language} language template e2e test - routing enabled`; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createLetterTemplate(props, name, language, inputFileName); + }); +} diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-letter-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-letter-e2e.ts new file mode 100644 index 00000000..c63e1df3 --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-letter-e2e.ts @@ -0,0 +1,35 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { TemplateMgmtLetterPage } from '../../pages/template-mgmt-letter-page'; +import { + startPage, + chooseTemplate, + createLetterTemplate, + startNewTemplate, + requestProof, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new letter template successfully - routing enabled`, async ({ + page, +}) => { + test.setTimeout(240_000); // override just for this test + const props = { + basePage: new TemplateMgmtBasePage(page), + letterPage: new TemplateMgmtLetterPage(page), + }; + const channel = 'Letter'; + const channelPath = 'letter'; + const name = 'letter template e2e test - routing enabled'; + const language = 'en'; + const inputFileName = 'template.pdf'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createLetterTemplate(props, name, language, inputFileName); + await requestProof(props, channelPath, true); +}); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-nhsapp-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-nhsapp-e2e.ts new file mode 100644 index 00000000..8eeaa93b --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-nhsapp-e2e.ts @@ -0,0 +1,28 @@ +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + previewPage, + startNewTemplate, + createNhsAppTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new nhsapp template successfully - routing enabled`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'NHS App message'; + const channelPath = 'nhs-app'; + const name = 'nhs app template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createNhsAppTemplate(page, name); + await previewPage(props, channelPath, name); +}); diff --git a/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-sms-e2e.ts b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-sms-e2e.ts new file mode 100644 index 00000000..29f42c7b --- /dev/null +++ b/tests/test-team/template-mgmt-e2e-tests/templates-with-routing-enabled/template-mgmt-sms-e2e.ts @@ -0,0 +1,30 @@ +/* eslint-disable security/detect-non-literal-regexp */ + +import { test } from '@playwright/test'; +import { TemplateMgmtBasePage } from '../../pages/template-mgmt-base-page'; +import { + startPage, + chooseTemplate, + previewPage, + startNewTemplate, + createSmsTemplate, +} from '../../functions/template-mgmt-e2e-common-steps'; + +test.use({ storageState: 'login-state/primaryRoutingEnabled.json' }); + +test(`User creates and submits a new sms template successfully - routing enabled`, async ({ + page, +}) => { + const props = { + basePage: new TemplateMgmtBasePage(page), + }; + const channel = 'Text message (SMS)'; + const channelPath = 'text-message'; + const name = 'SMS template e2e test - routing enabled'; + + await startPage(props); + await startNewTemplate(props); + await chooseTemplate(props, channel); + await createSmsTemplate(page, name); + await previewPage(props, channelPath, name); +});