Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ AWS_PROFILE_templates="value from ~/.aws/config"
AWS_PROFILE_auth="value from ~/.aws/config"

TARGET_ENVIRONMENT=<main, josm3>
SYSTEM_TESTS_CIS2_INT_CREDENTIALS="{"username":"x.y@nhs.net","password":"x","totpSecret":"x"}"
1,860 changes: 1,224 additions & 636 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion project.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
],
"cSpell.language": "en-GB",
"editor.wordWrap": "off",
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"[plaintext]": {
"editor.wordWrap": "off",
"editor.formatOnSave": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[A-Z]+s
Bitwarden
bot
Cognito
Expand Down
18 changes: 9 additions & 9 deletions tests/security/functions/common-steps.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { test, expect, Page } from '@playwright/test';
import { TemplateMgmtBasePage } from '../pages/template-mgmt-base-page';
import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page';
import { TemplateMgmtPdfLetterPage } from '../pages/template-mgmt-pdf-letter-page';

type CommonStepsProps = {
basePage: TemplateMgmtBasePage;
};

type CommonLetterStepsProps = CommonStepsProps & {
letterPage: TemplateMgmtLetterPage;
type CommonPdfLetterStepsProps = CommonStepsProps & {
pdfLetterPage: TemplateMgmtPdfLetterPage;
};

export function startPage({ basePage }: CommonStepsProps) {
Expand Down Expand Up @@ -50,8 +50,8 @@ export function chooseTemplate(
});
}

export function createLetterTemplate(
{ basePage, letterPage }: CommonLetterStepsProps,
export function createPdfLetterTemplate(
{ basePage, pdfLetterPage: letterPage }: CommonPdfLetterStepsProps,
name: string
) {
return test.step('Create template', async () => {
Expand All @@ -67,7 +67,7 @@ export function createLetterTemplate(

await letterPage.selectLetterOption('Letter type','x1');
await letterPage.selectLetterOption('Letter language','fr');
await letterPage.uploadLetterTemplate('Letter template PDF');
await letterPage.uploadPdfLetterTemplate('Letter template PDF');
});
}

Expand Down Expand Up @@ -285,8 +285,8 @@ export function submitPage(
});
}

export function submitTemplate(
{ basePage, letterPage }: CommonLetterStepsProps,
export function submitPdfLetterTemplate(
{ basePage, pdfLetterPage }: CommonPdfLetterStepsProps,
channelPath: string,
) {
return test.step('Submit template', async () => {
Expand All @@ -299,6 +299,6 @@ export function submitTemplate(

await basePage.checkStatus('Not yet submitted');

await letterPage.submitLetterTemplate();
await pdfLetterPage.submitLetterTemplate();
})
}
4 changes: 2 additions & 2 deletions tests/security/lifecycle/auth/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
import { users } from '../../fixtures/users';

async function main() {
const { lifecycleServiceDir, targetEnvrionment, runId } =
const { lifecycleServiceDir, targetEnvironment, runId } =
parseSetupTeardownArgs(process.argv);

const stateFile = new StateFile(lifecycleServiceDir, runId);

const authHelper = await AuthHelper.init(
targetEnvrionment,
targetEnvironment,
'security',
runId
);
Expand Down
4 changes: 2 additions & 2 deletions tests/security/lifecycle/auth/teardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import z from 'zod';
import { users } from '../../fixtures/users';

async function main() {
const { lifecycleServiceDir, targetEnvrionment, runId } =
const { lifecycleServiceDir, targetEnvironment, runId } =
parseSetupTeardownArgs(process.argv);

const stateFile = new StateFile(lifecycleServiceDir, runId);
await stateFile.loadFromDisk();

const authHelper = await AuthHelper.init(
targetEnvrionment,
targetEnvironment,
'security',
runId
);
Expand Down
29 changes: 20 additions & 9 deletions tests/security/lifecycle/templates/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import { clients } from '../../fixtures/clients';
import { randomUUID } from 'node:crypto';

async function main() {
const { lifecycleServiceDir, targetEnvrionment, runId } =
const { lifecycleServiceDir, targetEnvironment, runId } =
parseSetupTeardownArgs(process.argv);

const stateFile = new StateFile(lifecycleServiceDir, runId);

const sftpPollingFrequency = await increaseSftpPollingFrequency(
targetEnvrionment
);
const sftpPollingFrequency =
await increaseSftpPollingFrequency(targetEnvironment);

stateFile.setValue(
'initialState',
Expand All @@ -34,7 +33,7 @@ async function main() {

await Promise.all(
clientEntries.map(([, { id, config }]) =>
createClientConfig(targetEnvrionment, id, config, 'security')
createClientConfig(targetEnvironment, id, config, 'security')
)
);

Expand Down Expand Up @@ -62,7 +61,11 @@ async function main() {
message: 'multi-channel-routing-config-nhsapp-message',
}
);
stateFile.setValue('templates', 'multiChannelRoutingConfigNhsApp', multiChannelRoutingConfigNhsAppTemplate);
stateFile.setValue(
'templates',
'multiChannelRoutingConfigNhsApp',
multiChannelRoutingConfigNhsAppTemplate
);

const multiChannelRoutingConfigEmailTemplate = TemplateFactory.create(
randomUUID(),
Expand All @@ -74,7 +77,11 @@ async function main() {
subject: 'multi-channel-routing-config-email-template-subject',
}
);
stateFile.setValue('templates', 'multiChannelRoutingConfigEmail', multiChannelRoutingConfigEmailTemplate);
stateFile.setValue(
'templates',
'multiChannelRoutingConfigEmail',
multiChannelRoutingConfigEmailTemplate
);

const multiChannelRoutingConfigSmsTemplate = TemplateFactory.create(
randomUUID(),
Expand All @@ -85,9 +92,13 @@ async function main() {
message: 'multi-channel-routing-config-sms-template-message',
}
);
stateFile.setValue('templates', 'multiChannelRoutingConfigSms', multiChannelRoutingConfigSmsTemplate);
stateFile.setValue(
'templates',
'multiChannelRoutingConfigSms',
multiChannelRoutingConfigSmsTemplate
);

await new StorageHelper(`nhs-notify-${targetEnvrionment}-app-api-templates`, [
await new StorageHelper(`nhs-notify-${targetEnvironment}-app-api-templates`, [
smsTemplate,
multiChannelRoutingConfigNhsAppTemplate,
multiChannelRoutingConfigEmailTemplate,
Expand Down
20 changes: 13 additions & 7 deletions tests/security/lifecycle/templates/teardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import z from 'zod';

async function main() {
const { lifecycleServiceDir, targetEnvrionment, runId } =
const { lifecycleServiceDir, targetEnvironment, runId } =
parseSetupTeardownArgs(process.argv);

const stateFile = new StateFile(lifecycleServiceDir, runId);
Expand All @@ -23,7 +23,7 @@ async function main() {
);

await restoreSftpPollingFrequency(
targetEnvrionment,
targetEnvironment,
initialSftpPollingFrequency
).catch((error) => {
exit = 1;
Expand All @@ -37,7 +37,7 @@ async function main() {
)
);

await deleteClientConfigs(targetEnvrionment, clientIds).catch((error) => {
await deleteClientConfigs(targetEnvironment, clientIds).catch((error) => {
exit = 1;
console.error(error);
});
Expand All @@ -50,18 +50,24 @@ async function main() {

const deletedTemplates = await Promise.allSettled(
[...clientIds, cis2ClientId].map((id) =>
deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-templates`)
deleteClientEntries(
id,
`nhs-notify-${targetEnvironment}-app-api-templates`
)
)
);

const deletedRoutingConfigs = await Promise.allSettled(
[...clientIds, cis2ClientId].map((id) =>
deleteClientEntries(id, `nhs-notify-${targetEnvrionment}-app-api-routing-configuration`)
deleteClientEntries(
id,
`nhs-notify-${targetEnvironment}-app-api-routing-configuration`
)
)
);

const failures = [...deletedTemplates, ...deletedRoutingConfigs].flatMap((res) =>
res.status === 'rejected' ? [res.reason] : []
const failures = [...deletedTemplates, ...deletedRoutingConfigs].flatMap(
(res) => (res.status === 'rejected' ? [res.reason] : [])
);

if (failures.length) {
Expand Down
45 changes: 26 additions & 19 deletions tests/security/pages/template-mgmt-base-page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Locator, type Page, expect } from "@playwright/test";
import { Locator, type Page, expect } from '@playwright/test';

export class TemplateMgmtBasePage {
readonly page: Page;
Expand Down Expand Up @@ -37,31 +37,36 @@ export class TemplateMgmtBasePage {
this.loginLink = page.locator(`//a[text()='Sign in']`);

// Note: doing [class="nhsuk-back-link__link"] will not find the element if it has other class names
this.goBackLink = page.locator('#maincontent').getByRole('link', { name: 'Back to all templates' });
this.goBackLink = page
.locator('#maincontent')
.getByRole('link', { name: 'Back to all templates' });

this.pageHeader = page.getByRole("heading", { level: 1 });
this.pageHeader = page.getByRole('heading', { level: 1 });

this.errorSummary = page.getByRole("alert", { name: "There is a problem" });
this.errorSummary = page.getByRole('alert', { name: 'There is a problem' });

this.errorSummaryHeading = page.getByRole("heading", {
this.errorSummaryHeading = page.getByRole('heading', {
level: 2,
name: "There is a problem",
name: 'There is a problem',
});

this.errorSummaryList = this.errorSummary.getByRole("listitem");
this.errorSummaryList = this.errorSummary.getByRole('listitem');

this.submitButton = page.locator('button.nhsuk-button[type="submit"]');

this.submitTemplateButton = page.getByText("Submit template");
this.submitTemplateButton = page.getByText('Submit template');

this.templateToDelete = page.getByRole('link', { name: 'Test delete', exact: true });
this.templateToDelete = page.getByRole('link', {
name: 'Test delete',
exact: true,
});

this.templateEdited = (templateName: string) =>
page.getByRole('link', { name: templateName, exact: true });

this.skipLink = page
.locator('[id="skip-link"]')
.and(page.getByText("Skip to main content"));
.and(page.getByText('Skip to main content'));
}

async navigateTo(url: string) {
Expand All @@ -77,23 +82,23 @@ export class TemplateMgmtBasePage {
}

async noTemplatesAvailable() {
return await this.page.getByTestId("no-templates-available").isVisible();
return await this.page.getByTestId('no-templates-available').isVisible();
}

async clickButtonByName(buttonName: string) {
await this.page.getByRole("button", { name: buttonName }).click();
await this.page.getByRole('button', { name: buttonName }).click();
}

async clickLinkByName(linkName: string) {
await this.page.getByRole("link", { name: linkName, exact: true }).click();
async clickLinkByName(linkName: string, opts?: { exact?: true }) {
await this.page.getByRole('link', { name: linkName, ...opts }).click();
}

async clickSubmitButton() {
await this.submitButton.click();
}

async loadPage(_: string) {
throw new Error("Not implemented");
throw new Error('Not implemented');
}

async clickBackLink() {
Expand All @@ -110,7 +115,7 @@ export class TemplateMgmtBasePage {

async fillTextBox(textBoxName: string, textBoxContent: string) {
await this.page
.getByRole("textbox", { name: textBoxName })
.getByRole('textbox', { name: textBoxName })
.fill(textBoxContent);
}

Expand All @@ -119,7 +124,7 @@ export class TemplateMgmtBasePage {
}

async checkRadio(radioName: string) {
await this.page.getByRole("radio", { name: radioName }).check();
await this.page.getByRole('radio', { name: radioName }).check();
}

async tableRows() {
Expand All @@ -129,8 +134,10 @@ export class TemplateMgmtBasePage {
}

async clickFirstTableRowLink() {
const link = this.page.locator('table:nth-of-type(1) tr:nth-of-type(1) td:nth-of-type(1) a');
await expect(link).toContainText("COPY");
const link = this.page.locator(
'table:nth-of-type(1) tr:nth-of-type(1) td:nth-of-type(1) a'
);
await expect(link).toContainText('COPY');
await link.click();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TemplateMgmtBasePage } from './template-mgmt-base-page';
import fs from 'fs';
import path from 'path';

export class TemplateMgmtLetterPage extends TemplateMgmtBasePage {
export class TemplateMgmtPdfLetterPage extends TemplateMgmtBasePage {
readonly page: Page;

constructor(page: Page) {
Expand All @@ -15,7 +15,7 @@ export class TemplateMgmtLetterPage extends TemplateMgmtBasePage {
await this.page.getByLabel(labelName).selectOption(optionName);
}

async uploadLetterTemplate(templateName: string) {
async uploadPdfLetterTemplate(templateName: string) {
const maxRetries = 40;
const retryInterval = 2000;

Expand Down
4 changes: 2 additions & 2 deletions tests/security/tests/template-mgmt-cis2-login.security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
startPage,
} from '../functions/common-steps';
import test from 'playwright/test';
import { TemplateMgmtLetterPage } from '../pages/template-mgmt-letter-page';
import { TemplateMgmtPdfLetterPage } from '../pages/template-mgmt-pdf-letter-page';

test.use({ storageState: { cookies: [], origins: [] } });
test.setTimeout(300_000);
Expand All @@ -30,7 +30,7 @@ test('User logs in via CIS2, saves data in templates, logs out and logs back in
context,
}) => {
const basePage = new TemplateMgmtBasePage(page);
const letterPage = new TemplateMgmtLetterPage(page);
const letterPage = new TemplateMgmtPdfLetterPage(page);
const props = {
basePage,
letterPage,
Expand Down
Loading
Loading