Skip to content

Commit 37f8596

Browse files
committed
CCM-12666: WIP - playwright accessibility tests
1 parent f844fc9 commit 37f8596

File tree

10 files changed

+227
-4
lines changed

10 files changed

+227
-4
lines changed

package-lock.json

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import path from 'node:path';
2+
import { defineConfig, devices } from '@playwright/test';
3+
import baseConfig from '../playwright.config';
4+
5+
const buildCommand = [
6+
'INCLUDE_AUTH_PAGES=true',
7+
'npm run build && npm run start',
8+
].join(' ');
9+
10+
export default defineConfig({
11+
...baseConfig,
12+
fullyParallel: true,
13+
timeout: 60_000, // 30 seconds in the playwright default
14+
expect: {
15+
timeout: 10_000, // default is 5 seconds. After creating and previewing sometimes the load is slow on a cold start
16+
},
17+
projects: [
18+
{
19+
name: 'accessibility:setup',
20+
testMatch: 'ui.setup.ts',
21+
use: {
22+
baseURL: 'http://localhost:3000',
23+
...devices['Desktop Chrome'],
24+
headless: true,
25+
screenshot: 'only-on-failure',
26+
},
27+
},
28+
{
29+
name: 'accessibility',
30+
testMatch: '*.accessibility.spec.ts',
31+
use: {
32+
screenshot: 'only-on-failure',
33+
baseURL: 'http://localhost:3000',
34+
...devices['Desktop Chrome'],
35+
headless: false,
36+
launchOptions: {
37+
slowMo: 200,
38+
},
39+
storageState: path.resolve(__dirname, '../.auth/user.json'),
40+
},
41+
dependencies: ['accessibility:setup'],
42+
teardown: 'accessibility:teardown',
43+
},
44+
{
45+
name: 'accessibility:teardown',
46+
testMatch: 'ui.teardown.ts',
47+
},
48+
],
49+
/* Run your local dev server before starting the tests */
50+
webServer: {
51+
timeout: 2 * 60 * 1000, // 2 minutes
52+
command: buildCommand,
53+
cwd: path.resolve(__dirname, '../../../..'),
54+
url: 'http://localhost:3000/templates/create-and-submit-templates',
55+
reuseExistingServer: !process.env.CI,
56+
stderr: 'pipe',
57+
},
58+
});

tests/test-team/config/playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export default defineConfig({
99
forbidOnly: !!process.env.CI,
1010
retries: 0,
1111
/* Opt out of parallel tests on CI. */
12-
workers: process.env.CI ? 4 : undefined,
12+
workers: process.env.CI ? 4 : 1,
1313
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
1414
reporter: [
1515
['line'],
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { test as base } from '@playwright/test';
2+
import AxeBuilder from '@axe-core/playwright';
3+
4+
export type AxeFixture = {
5+
makeAxeBuilder: () => AxeBuilder;
6+
};
7+
8+
/*
9+
* https://dequeuniversity.com/rules/axe/html/4.11 - search for wcag131
10+
*/
11+
const PRINCIPLE_1_GUIDELINE_1_1_3_1_AAA = [
12+
'aria-hidden-body',
13+
'aria-required-children',
14+
'aria-required-parent',
15+
'definition-list',
16+
'dlitem',
17+
'list',
18+
'listitem',
19+
'p-as-heading',
20+
'table-fake-caption',
21+
'td-has-header',
22+
'td-headers-attr',
23+
'th-has-data-cells',
24+
];
25+
26+
export const test = base.extend<AxeFixture>({
27+
makeAxeBuilder: async ({ page }, use) => {
28+
const makeAxeBuilder = () =>
29+
new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']); // This list is inclusive
30+
31+
await use(makeAxeBuilder);
32+
},
33+
});
34+
export { expect } from '@playwright/test';

tests/test-team/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"@faker-js/faker": "^9.9.0",
1212
"@nhsdigital/nhs-notify-event-schemas-template-management": "*",
1313
"@playwright/test": "^1.51.1",
14+
"@axe-core/playwright": "^4.11.0",
15+
"axe-core": "^4.11.0",
1416
"async-mutex": "^0.5.0",
1517
"aws-amplify": "^6.13.6",
1618
"date-fns": "^4.1.0",
@@ -30,6 +32,7 @@
3032
"scripts": {
3133
"lint": "eslint .",
3234
"lint:fix": "npm run lint -- --fix",
35+
"test:accessibility": "playwright test --project accessibility -c config/accessibility/accessibility.config.ts",
3336
"test:api": "playwright test --project api -c config/api/api.config.ts",
3437
"test:e2e": "playwright test --project e2e -c config/e2e/e2e.config.ts",
3538
"test:event": "playwright test -c config/event/event.config.ts",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './campaign-id-required-page';
2+
export * from './choose-message-order-page';
3+
export * from './create-message-plan-page';
4+
export * from './message-plans-page';

tests/test-team/pages/routing-message-plans-page.ts renamed to tests/test-team/pages/routing/message-plans-page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Locator, type Page } from '@playwright/test';
2-
import { TemplateMgmtBasePageNonDynamic } from './template-mgmt-base-page-non-dynamic';
2+
import { TemplateMgmtBasePageNonDynamic } from '../template-mgmt-base-page-non-dynamic';
33

44
export class RoutingMessagePlansPage extends TemplateMgmtBasePageNonDynamic {
55
static readonly pageUrlSegment = 'message-plans';
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
createAuthHelper,
3+
TestUser,
4+
testUsers,
5+
} from 'helpers/auth/cognito-auth-helper';
6+
import { RoutingConfigFactory } from 'helpers/factories/routing-config-factory';
7+
import { RoutingConfigStorageHelper } from 'helpers/db/routing-config-storage-helper';
8+
import { test, expect } from 'fixtures/axe-test';
9+
import {
10+
RoutingChooseMessageOrderPage,
11+
RoutingCreateMessagePlanPage,
12+
RoutingMessagePlanCampaignIdRequiredPage,
13+
RoutingMessagePlansPage,
14+
} from 'pages/routing';
15+
import { loginAsUser } from 'helpers/auth/login-as-user';
16+
import { TemplateMgmtBasePage } from 'pages/template-mgmt-base-page';
17+
import AxeBuilder from '@axe-core/playwright';
18+
19+
let user: TestUser;
20+
let userWithMultipleCampaigns: TestUser;
21+
22+
const routingStorageHelper = new RoutingConfigStorageHelper();
23+
24+
async function run<T extends TemplateMgmtBasePage>(
25+
page: T,
26+
makeAxeBuilder: () => AxeBuilder,
27+
fn?: (page: T) => Promise<void>
28+
) {
29+
await page.loadPage();
30+
if (fn) {
31+
await fn(page);
32+
}
33+
const results = await makeAxeBuilder().analyze();
34+
expect(results.violations).toEqual([]);
35+
}
36+
37+
test.describe('Routing - Accessibility', () => {
38+
test.beforeAll(async () => {
39+
const authHelper = createAuthHelper();
40+
41+
user = await authHelper.getTestUser(testUsers.User1.userId);
42+
userWithMultipleCampaigns = await authHelper.getTestUser(
43+
testUsers.UserWithMultipleCampaigns.userId
44+
);
45+
await routingStorageHelper.seed([
46+
RoutingConfigFactory.create(user).dbEntry,
47+
]);
48+
});
49+
50+
test.afterAll(async () => {
51+
await routingStorageHelper.deleteSeeded();
52+
});
53+
54+
test('Message plans', async ({ page, makeAxeBuilder }) =>
55+
run(new RoutingMessagePlansPage(page), makeAxeBuilder));
56+
57+
test('Campaign required', async ({ page, makeAxeBuilder }) =>
58+
run(new RoutingMessagePlanCampaignIdRequiredPage(page), makeAxeBuilder));
59+
60+
test('Choose message order', async ({ page, makeAxeBuilder }) =>
61+
run(new RoutingChooseMessageOrderPage(page), makeAxeBuilder));
62+
63+
test('Choose message order - error', async ({ page, makeAxeBuilder }) =>
64+
run(new RoutingChooseMessageOrderPage(page), makeAxeBuilder, (p) =>
65+
p.clickContinueButton()
66+
));
67+
68+
test.describe('client has multiple campaigns', () => {
69+
test.use({ storageState: { cookies: [], origins: [] } });
70+
71+
test.beforeEach(async ({ page }) => {
72+
await loginAsUser(userWithMultipleCampaigns, page);
73+
});
74+
75+
test('Create message plan', async ({ page, makeAxeBuilder }) =>
76+
run(
77+
new RoutingCreateMessagePlanPage(page, {
78+
messageOrder: 'NHSAPP,EMAIL,SMS,LETTER',
79+
}),
80+
makeAxeBuilder
81+
));
82+
83+
test('Create message plan - error', async ({ page, makeAxeBuilder }) =>
84+
run(
85+
new RoutingCreateMessagePlanPage(page, {
86+
messageOrder: 'NHSAPP,EMAIL,SMS,LETTER',
87+
}),
88+
makeAxeBuilder,
89+
(p) => p.clickSubmit()
90+
));
91+
});
92+
});

tests/test-team/template-mgmt-component-tests/template-protected-routes.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { TemplateMgmtUploadLetterMissingCampaignClientIdPage } from '../pages/le
3636
import { RoutingChooseMessageOrderPage } from '../pages/routing/choose-message-order-page';
3737
import { RoutingCreateMessagePlanPage } from '../pages/routing/create-message-plan-page';
3838
import { RoutingMessagePlanCampaignIdRequiredPage } from '../pages/routing/campaign-id-required-page';
39-
import { RoutingMessagePlansPage } from '../pages/routing-message-plans-page';
39+
import { RoutingMessagePlansPage } from '../pages/routing/message-plans-page';
4040

4141
// Reset storage state for this file to avoid being authenticated
4242
test.use({ storageState: { cookies: [], origins: [] } });

tests/test-team/template-mgmt-routing-component-tests/message-plans.routing-component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, expect } from '@playwright/test';
2-
import { RoutingMessagePlansPage } from '../pages/routing-message-plans-page';
2+
import { RoutingMessagePlansPage } from '../pages/routing/message-plans-page';
33
import {
44
assertFooterLinks,
55
assertSignOutLink,

0 commit comments

Comments
 (0)