Skip to content

Commit ad7201a

Browse files
committed
CCM-11474: playwright
1 parent 57f823c commit ad7201a

File tree

9 files changed

+315
-9
lines changed

9 files changed

+315
-9
lines changed

frontend/src/__tests__/components/forms/MessagePlan/__snapshots__/MessagePlan.test.tsx.snap

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ exports[`renders error if form is submitted with empty name 1`] = `
180180
type="hidden"
181181
value="campaign-id"
182182
/>
183-
<p>
183+
<p
184+
data-testid="single-campaign-id"
185+
>
184186
campaign-id
185187
</p>
186188
</div>
@@ -326,7 +328,9 @@ exports[`renders error if form is submitted with name too long 1`] = `
326328
type="hidden"
327329
value="campaign-id"
328330
/>
329-
<p>
331+
<p
332+
data-testid="single-campaign-id"
333+
>
330334
campaign-id
331335
</p>
332336
</div>
@@ -775,7 +779,9 @@ exports[`renders form with single campaign id displayed 1`] = `
775779
type="hidden"
776780
value="campaign-id"
777781
/>
778-
<p>
782+
<p
783+
data-testid="single-campaign-id"
784+
>
779785
campaign-id
780786
</p>
781787
</div>

frontend/src/components/forms/MessagePlan/MessagePlan.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export function MessagePlanForm({
6464
</Details>
6565
<TextInput
6666
id='name'
67+
name='name'
6768
value={name}
6869
onChange={handleNameChange}
6970
error={state.errorState?.fieldErrors?.name?.join(',')}
@@ -81,14 +82,15 @@ export function MessagePlanForm({
8182
value={campaignIds[0]}
8283
readOnly
8384
/>
84-
<p>{campaignIds[0]}</p>
85+
<p data-testid='single-campaign-id'>{campaignIds[0]}</p>
8586
</>
8687
) : (
8788
<Select
89+
id='campaignId'
90+
name='campaignId'
8891
label={formContent.fields.campaignId.label}
8992
labelProps={{ size: 's' }}
9093
hint={formContent.fields.campaignId.hintMulti}
91-
id='campaignId'
9294
defaultValue={campaignId}
9395
onChange={handleCampaignIdChange}
9496
error={state.errorState?.fieldErrors?.campaignId?.join(',')}

tests/test-team/pages/routing-choose-message-order-page.ts renamed to tests/test-team/pages/routing/choose-message-order-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 RoutingChooseMessageOrderPage extends TemplateMgmtBasePageNonDynamic {
55
static readonly pageUrlSegment = 'message-plans/choose-message-order';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Locator, type Page } from '@playwright/test';
2+
import { TemplateMgmtBasePageNonDynamic } from '../template-mgmt-base-page-non-dynamic';
3+
4+
export class CreateMessagePlanPage extends TemplateMgmtBasePageNonDynamic {
5+
static readonly pageUrlSegment = 'message-plans/create-message-plan';
6+
7+
readonly submitButton: Locator;
8+
9+
readonly goBackLink: Locator;
10+
11+
readonly nameField: Locator;
12+
13+
readonly nameFieldError: Locator;
14+
15+
readonly campaignIdSelector: Locator;
16+
17+
readonly campaignIdFieldError: Locator;
18+
19+
readonly singleCampaignIdElement: Locator;
20+
21+
constructor(page: Page, queryParameters?: { messageOrder: string }) {
22+
super(page);
23+
this.submitButton = page.locator('button.nhsuk-button[type="submit"]', {
24+
hasText: 'Save and continue',
25+
});
26+
this.goBackLink = page.getByText('Go back');
27+
this.nameField = page.getByTestId('name-field');
28+
this.campaignIdSelector = page.getByTestId('campaign-id-field');
29+
this.singleCampaignIdElement = page.getByTestId('single-campaign-id');
30+
31+
if (queryParameters) {
32+
this.queryParameters = new URLSearchParams(queryParameters);
33+
}
34+
35+
this.nameFieldError = page.locator('#name--error-message');
36+
37+
this.campaignIdFieldError = page.locator('#campaignId--error-message');
38+
}
39+
40+
async clickSubmit() {
41+
await this.submitButton.click();
42+
}
43+
}

tests/test-team/pages/template-mgmt-base-page-non-dynamic.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export abstract class TemplateMgmtBasePageNonDynamic extends TemplateMgmtBasePag
1616
throw new Error('pageUrlSegment is not defined');
1717
}
1818

19-
await this.navigateTo(`/${appUrlSegment}/${pageUrlSegment}`);
19+
let url = `/${appUrlSegment}/${pageUrlSegment}`;
20+
21+
if (this.queryParameters) {
22+
url += `?${this.queryParameters.toString()}`;
23+
}
24+
25+
await this.navigateTo(url);
2026
}
2127
}

tests/test-team/pages/template-mgmt-base-page.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export abstract class TemplateMgmtBasePage {
77

88
static readonly pageUrlSegment: string;
99

10+
queryParameters?: URLSearchParams;
11+
1012
readonly header: Locator;
1113

1214
readonly headerLogoLink: Locator;

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
@@ -33,7 +33,7 @@ import { TemplateMgmtTemplateSubmittedLetterPage } from '../pages/letter/templat
3333
import { TemplateMgmtTemplateSubmittedNhsAppPage } from '../pages/nhs-app/template-mgmt-template-submitted-nhs-app-page';
3434
import { TemplateMgmtTemplateSubmittedSmsPage } from '../pages/sms/template-mgmt-template-submitted-sms-page';
3535
import { TemplateMgmtUploadLetterMissingCampaignClientIdPage } from '../pages/letter/template-mgmt-upload-letter-missing-campaign-client-id-page';
36-
import { RoutingChooseMessageOrderPage } from '../pages/routing-choose-message-order-page';
36+
import { RoutingChooseMessageOrderPage } from '../pages/routing/choose-message-order-page';
3737

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

tests/test-team/template-mgmt-routing-component-tests/choose-message-order.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 { RoutingChooseMessageOrderPage } from '../pages/routing-choose-message-order-page';
2+
import { RoutingChooseMessageOrderPage } from '../pages/routing/choose-message-order-page';
33
import {
44
assertFooterLinks,
55
assertGoBackLink,
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import { test, expect } from '@playwright/test';
2+
import {
3+
createAuthHelper,
4+
testUsers,
5+
type TestUser,
6+
} from 'helpers/auth/cognito-auth-helper';
7+
import { loginAsUser } from 'helpers/auth/login-as-user';
8+
import { RoutingConfigStorageHelper } from 'helpers/db/routing-config-storage-helper';
9+
import {
10+
assertFooterLinks,
11+
assertGoBackLink,
12+
assertSignOutLink,
13+
assertHeaderLogoLink,
14+
assertSkipToMainContent,
15+
} from 'helpers/template-mgmt-common.steps';
16+
import { CreateMessagePlanPage } from 'pages/routing/create-message-plan-page';
17+
18+
const routingConfigStorageHelper = new RoutingConfigStorageHelper();
19+
20+
let user: TestUser;
21+
let userWithoutCampaignId: TestUser;
22+
let userWithMultipleCampaigns: TestUser;
23+
24+
test.beforeAll(async () => {
25+
const authHelper = createAuthHelper();
26+
user = await authHelper.getTestUser(testUsers.User1.userId);
27+
userWithoutCampaignId = await authHelper.getTestUser(testUsers.User6.userId);
28+
userWithMultipleCampaigns = await authHelper.getTestUser(
29+
testUsers.UserWithMultipleCampaigns.userId
30+
);
31+
});
32+
33+
test.afterAll(async () => {
34+
await routingConfigStorageHelper.deleteAdHoc();
35+
});
36+
37+
test.describe('Create Message Plan Page', () => {
38+
for (const messageOrder of [
39+
'NHSAPP',
40+
'NHSAPP,EMAIL',
41+
'NHSAPP,SMS',
42+
'NHSAPP,EMAIL,SMS',
43+
'NHSAPP,SMS,EMAIL',
44+
'NHSAPP,SMS,LETTER',
45+
'NHSAPP,EMAIL,SMS,LETTER',
46+
'LETTER',
47+
])
48+
test.describe(`with the messageOrder query parameter set to ${messageOrder}`, () => {
49+
test('common page tests', async ({ page, baseURL }) => {
50+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
51+
messageOrder,
52+
});
53+
54+
await createMessagePlanPage.loadPage();
55+
56+
await expect(page).toHaveURL(
57+
`${baseURL}/templates/message-plans/create-message-plan?messageOrder=${encodeURIComponent(messageOrder)}`
58+
);
59+
await expect(createMessagePlanPage.pageHeading).toHaveText(
60+
'Create a message plan'
61+
);
62+
63+
const props = {
64+
page: createMessagePlanPage,
65+
id: '',
66+
baseURL,
67+
expectedUrl: 'templates/message-plans/choose-message-order', // expected backlink url
68+
};
69+
70+
await assertSkipToMainContent(props);
71+
await assertHeaderLogoLink(props);
72+
await assertFooterLinks(props);
73+
await assertSignOutLink(props);
74+
await assertGoBackLink(props);
75+
});
76+
77+
test.describe('single campaign client', () => {
78+
test('creates a message plan and redirects to the template selection page for the created template', async ({
79+
page,
80+
}) => {
81+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
82+
messageOrder,
83+
});
84+
85+
await createMessagePlanPage.loadPage();
86+
87+
await createMessagePlanPage.nameField.fill('My Message Plan');
88+
89+
await expect(
90+
createMessagePlanPage.singleCampaignIdElement
91+
).toHaveText(user.campaignIds?.at(0) as string);
92+
93+
await expect(createMessagePlanPage.campaignIdSelector).toHaveCount(0);
94+
95+
await createMessagePlanPage.clickSubmit();
96+
97+
const chooseTemplatesRegexp =
98+
/\/message-plans\/choose-templates\/([\dA-Fa-f-]+)$/;
99+
100+
await expect(page).toHaveURL(chooseTemplatesRegexp);
101+
102+
const urlParts = page.url().match(chooseTemplatesRegexp);
103+
104+
routingConfigStorageHelper.addAdHocKey({
105+
id: urlParts![1],
106+
clientId: user.clientId,
107+
});
108+
});
109+
110+
test('displays error if name is empty', async ({ page }) => {
111+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
112+
messageOrder,
113+
});
114+
115+
await createMessagePlanPage.loadPage();
116+
117+
await createMessagePlanPage.clickSubmit();
118+
119+
await expect(createMessagePlanPage.nameFieldError).toHaveText(
120+
'Error: Enter a message plan name'
121+
);
122+
});
123+
124+
test('displays error if name is too long', async ({ page }) => {
125+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
126+
messageOrder,
127+
});
128+
129+
await createMessagePlanPage.loadPage();
130+
131+
await createMessagePlanPage.nameField.fill('x'.repeat(201));
132+
133+
await createMessagePlanPage.clickSubmit();
134+
135+
await expect(createMessagePlanPage.nameFieldError).toHaveText(
136+
'Error: Message plan name too long'
137+
);
138+
});
139+
});
140+
141+
test.describe('client has multiple campaigns', () => {
142+
test.use({ storageState: { cookies: [], origins: [] } });
143+
144+
test.beforeEach(async ({ page }) => {
145+
await loginAsUser(userWithMultipleCampaigns, page);
146+
});
147+
148+
test('creates a message plan and redirects to the template selection page for the created template', async ({
149+
page,
150+
}) => {
151+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
152+
messageOrder,
153+
});
154+
155+
await createMessagePlanPage.loadPage();
156+
157+
await createMessagePlanPage.nameField.fill('My Message Plan');
158+
159+
await expect(
160+
createMessagePlanPage.singleCampaignIdElement
161+
).toHaveCount(0);
162+
163+
await createMessagePlanPage.campaignIdSelector.selectOption(
164+
userWithMultipleCampaigns.campaignIds?.at(0) as string
165+
);
166+
167+
await createMessagePlanPage.clickSubmit();
168+
169+
const chooseTemplatesRegexp =
170+
/\/message-plans\/choose-templates\/([\dA-Fa-f-]+)$/;
171+
172+
await expect(page).toHaveURL(chooseTemplatesRegexp);
173+
174+
const urlParts = page.url().match(chooseTemplatesRegexp);
175+
176+
routingConfigStorageHelper.addAdHocKey({
177+
id: urlParts![1],
178+
clientId: userWithMultipleCampaigns.clientId,
179+
});
180+
});
181+
182+
test('displays error if campaign id is not selected', async ({
183+
page,
184+
}) => {
185+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
186+
messageOrder,
187+
});
188+
189+
await createMessagePlanPage.loadPage();
190+
191+
await createMessagePlanPage.nameField.fill('My Message Plan');
192+
193+
await createMessagePlanPage.clickSubmit();
194+
195+
await expect(createMessagePlanPage.campaignIdFieldError).toHaveText(
196+
'Error: Select a campaign'
197+
);
198+
});
199+
});
200+
201+
test.describe('client has no campaign id', () => {
202+
test.use({ storageState: { cookies: [], origins: [] } });
203+
204+
test.beforeEach(async ({ page }) => {
205+
await loginAsUser(userWithoutCampaignId, page);
206+
});
207+
208+
test('redirects to invalid config page', async ({ baseURL, page }) => {
209+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
210+
messageOrder,
211+
});
212+
213+
await createMessagePlanPage.loadPage();
214+
215+
await expect(page).toHaveURL(
216+
`${baseURL}/templates/message-plans/campaign-id-required`
217+
);
218+
});
219+
});
220+
});
221+
222+
test.describe('with no messageOrder query parameter set', () => {
223+
test('redirects to message order page', async ({ page, baseURL }) => {
224+
const createMessagePlanPage = new CreateMessagePlanPage(page);
225+
226+
await createMessagePlanPage.loadPage();
227+
228+
await expect(page).toHaveURL(
229+
`${baseURL}/templates/message-plans/choose-message-order`
230+
);
231+
});
232+
});
233+
234+
test.describe('with invalid messageOrder query parameter set', () => {
235+
test('redirects to message order page', async ({ page, baseURL }) => {
236+
const createMessagePlanPage = new CreateMessagePlanPage(page, {
237+
messageOrder: 'INVALID',
238+
});
239+
240+
await createMessagePlanPage.loadPage();
241+
242+
await expect(page).toHaveURL(
243+
`${baseURL}/templates/message-plans/choose-message-order`
244+
);
245+
});
246+
});
247+
});

0 commit comments

Comments
 (0)