Skip to content

Commit db69f79

Browse files
✅ - test: improve coverage for frontend
1 parent e0e9b6b commit db69f79

File tree

8 files changed

+275
-430
lines changed

8 files changed

+275
-430
lines changed

frontend/.storybook/decorators.tsx

+15-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const ReactRouterDecorator = (
2929
Story: StoryFn,
3030
{ parameters }: StoryContext,
3131
) => {
32+
const { id, ...params } = parameters.reactRouterDecorator?.route || {};
3233
const router = createBrowserRouter([
3334
{
3435
path: "",
@@ -39,9 +40,22 @@ export const ReactRouterDecorator = (
3940
},
4041
children: [
4142
{
43+
id: id,
4244
path: "/iframe.html",
4345
element: <Story />,
44-
...parameters.reactRouterDecorator?.route,
46+
...params,
47+
},
48+
{
49+
path: "*?", // On redirect.
50+
Component: () => {
51+
const container = window.frameElement
52+
? // @ts-expect-error - This does exist.
53+
window.frameElement.contentWindow
54+
: window;
55+
container.history.go(-1);
56+
},
57+
...params,
58+
id: "foo",
4559
},
4660
],
4761
},

frontend/.storybook/mockData.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { auditLogFactory } from "../src/fixtures/auditLog";
22
import { coReviewsFactory } from "../src/fixtures/coReview";
3-
import { destructionListAssigneesFactory } from "../src/fixtures/destructionList";
3+
import {
4+
FIXTURE_DESTRUCTION_LIST,
5+
destructionListAssigneesFactory,
6+
} from "../src/fixtures/destructionList";
7+
import { FIXTURE_DESTRUCTION_LIST_ITEM } from "../src/fixtures/destructionListItem";
48
import { FIXTURE_SELECTIELIJSTKLASSE_CHOICES } from "../src/fixtures/selectieLijstKlasseChoices";
59
import { userFactory, usersFactory } from "../src/fixtures/user";
610
import { zaaktypeChoicesFactory } from "../src/fixtures/zaaktypeChoices";
711

812
export const MOCKS = {
13+
// READS
914
AUDIT_LOG: {
1015
url: "http://localhost:8000/api/v1/logs?destruction-list=00000000-0000-0000-0000-000000000000",
1116
method: "GET",
@@ -93,6 +98,38 @@ export const MOCKS = {
9398
status: 200,
9499
response: zaaktypeChoicesFactory(),
95100
},
101+
102+
// WRITES
103+
DESTRUCTION_LIST_CREATE: {
104+
url: "http://localhost:8000/api/v1/destruction-lists/",
105+
method: "POST",
106+
status: "201",
107+
response: FIXTURE_DESTRUCTION_LIST,
108+
},
109+
DESTRUCTION_LIST_UPDATE: {
110+
url: "http://localhost:8000/api/v1/destruction-lists/undefined", // FIXME
111+
method: "PATCH",
112+
status: "200",
113+
response: FIXTURE_DESTRUCTION_LIST,
114+
},
115+
DESTRUCTION_LIST_DELETE: {
116+
url: "http://localhost:8000/api/v1/destruction-lists/00000000-0000-0000-0000-000000000000", // FIXME
117+
method: "DELETE",
118+
status: "200",
119+
response: {},
120+
},
121+
DESTRUCTION_LIST_PROCESS_ABORT: {
122+
url: "http://localhost:8000/api/v1/destruction-lists/00000000-0000-0000-0000-000000000000/abort",
123+
method: "POST",
124+
status: "200",
125+
response: {},
126+
},
127+
REVIEW_RESPONSE_CREATE: {
128+
url: "http://localhost:8000/api/v1/review-responses/",
129+
method: "POST",
130+
status: "201",
131+
response: {},
132+
},
96133
};
97134

98135
export const MOCK_BASE = Object.values(MOCKS);

frontend/.storybook/playFunctions.ts

+18-59
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { PlayFunction } from "@storybook/types";
99
/**
1010
* Selects and deselects the "Identificatie" column and asserts whether it's shown/hidden based on the selected state.
1111
* @param canvasElement
12+
* @param parameters
1213
*/
1314
export const assertCheckboxSelection: PlayFunction<ReactRenderer> = async ({
1415
canvasElement,
@@ -49,7 +50,7 @@ export const assertCheckboxSelection: PlayFunction<ReactRenderer> = async ({
4950
}
5051

5152
// Stop if forwards direction is used.
52-
if (parameters?.direction === "forwards") {
53+
if (parameters?.assertCheckboxSelection?.direction === "forwards") {
5354
return;
5455
}
5556

@@ -149,7 +150,7 @@ export const clickButton: PlayFunction<ReactRenderer> = async (context) => {
149150
await clickElement({
150151
...context,
151152
parameters: {
152-
...context.parameters,
153+
clickElement: { role: "button", ...context.parameters?.clickButton },
153154
role: "button",
154155
},
155156
});
@@ -163,7 +164,7 @@ export const clickCheckbox: PlayFunction<ReactRenderer> = async (context) => {
163164
await clickElement({
164165
...context,
165166
parameters: {
166-
...context.parameters,
167+
clickElement: { role: "checkbox", ...context.parameters.clickCheckbox },
167168
role: "checkbox",
168169
},
169170
});
@@ -175,12 +176,12 @@ export const clickCheckbox: PlayFunction<ReactRenderer> = async (context) => {
175176
*/
176177
export const clickElement: PlayFunction<ReactRenderer> = async (context) => {
177178
const {
178-
checked,
179+
checked = false,
179180
elementIndex = 0,
180181
inTBody = false,
181182
role,
182183
name,
183-
} = context.parameters as ClickElementParameters;
184+
} = context.parameters.clickElement as ClickElementParameters;
184185

185186
console.assert(
186187
role,
@@ -201,13 +202,6 @@ export const clickElement: PlayFunction<ReactRenderer> = async (context) => {
201202

202203
const element = elements[elementIndex];
203204

204-
const checkedState = (element as HTMLInputElement).checked;
205-
206-
// Normalize state.
207-
if (typeof checked !== "undefined" && checked === checkedState) {
208-
await userEvent.click(element, { delay: 10 });
209-
}
210-
211205
await userEvent.click(element, { delay: 10 });
212206
};
213207

@@ -225,14 +219,22 @@ export const fillForm: PlayFunction<ReactRenderer> = async (context) => {
225219
const canvas = within(context.canvasElement);
226220

227221
const {
228-
form = await canvas.findByRole("form"),
222+
form = (await canvas.queryByRole("form")) ||
223+
(await canvas.findByRole("dialog")), // Fixme
229224
formValues = {},
230225
submitForm = true,
231-
} = context.parameters as FillFormParameters;
226+
} = context.parameters.fillForm as FillFormParameters;
227+
228+
await waitFor(() => expect(form).toBeVisible());
232229

233230
for (const [name, value] of Object.entries(formValues)) {
234-
const field: HTMLInputElement | HTMLSelectElement =
235-
await within(form).findByLabelText(name);
231+
const fields: (HTMLInputElement | HTMLSelectElement)[] =
232+
await within(form).findAllByLabelText(name);
233+
234+
const field =
235+
fields.length > 1
236+
? await within(form).findByLabelText(value as string)
237+
: fields[0]; // Exception if not found.
236238

237239
switch (typeof value) {
238240
case "boolean":
@@ -261,46 +263,3 @@ export const fillForm: PlayFunction<ReactRenderer> = async (context) => {
261263
await userEvent.click(submit, { delay: 100 });
262264
}
263265
};
264-
265-
type FillConfirmationFormParameters = ClickElementParameters &
266-
FillFormParameters;
267-
268-
/**
269-
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
270-
* Then fills in dialog form, submits if `submitForm` is truthy.
271-
* @param context
272-
*/
273-
export const fillButtonConfirmationForm: PlayFunction<ReactRenderer> = async (
274-
context,
275-
) => {
276-
await fillConfirmationForm({
277-
...context,
278-
parameters: { ...context.parameters, role: "button" },
279-
});
280-
};
281-
282-
/**
283-
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
284-
* Then fills in dialog form, submits if `submitForm` is truthy.
285-
* @param context
286-
*/
287-
export const fillConfirmationForm: PlayFunction<ReactRenderer> = async (
288-
context,
289-
) => {
290-
const parameters = context.parameters as FillConfirmationFormParameters;
291-
const _context = { ...context, parameters };
292-
await clickElement(_context);
293-
294-
const canvas = within(context.canvasElement);
295-
const modal = await canvas.findByRole("dialog");
296-
// FIXME: Fix in admin-ui form should be picked up by role.
297-
// const form = await within(modal).findByRole("form", {}, { timeout: 3000 });
298-
299-
await fillForm({
300-
..._context,
301-
parameters: {
302-
...parameters,
303-
form: modal,
304-
},
305-
});
306-
};

frontend/src/components/DestructionListReviewer/DestructionListReviewer.stories.tsx

+14-10
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,14 @@ const assertEditReviewer: PlayFunction<ReactRenderer> = async (context) => {
105105
await fillForm({
106106
...context,
107107
parameters: {
108-
form: form,
109-
formValues: {
110-
Beoordelaar: "Proces ei Genaar (Proces ei Genaar)",
111-
Reden: "Edit reviewer",
108+
fillForm: {
109+
form: form,
110+
formValues: {
111+
Beoordelaar: "Proces ei Genaar (Proces ei Genaar)",
112+
Reden: "Edit reviewer",
113+
},
114+
submitForm: true,
112115
},
113-
submitForm: true,
114116
},
115117
});
116118
await waitFor(() => {
@@ -149,12 +151,14 @@ const assertEditCoReviewers: PlayFunction<ReactRenderer> = async (context) => {
149151
await fillForm({
150152
...context,
151153
parameters: {
152-
form: form,
153-
formValues: {
154-
"Medebeoordelaar 2": "Co Reviewer (co-reviewer)",
155-
Reden: "Edit co-reviewers",
154+
fillForm: {
155+
form: form,
156+
formValues: {
157+
"Medebeoordelaar 2": "Co Reviewer (co-reviewer)",
158+
Reden: "Edit co-reviewers",
159+
},
160+
submitForm: true,
156161
},
157-
submitForm: true,
158162
},
159163
});
160164

frontend/src/fixtures/destructionList.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createArrayFactory, createObjectFactory } from "./factory";
66
import { defaultAssignees } from "./reviewers";
77
import { beoordelaarFactory, procesEigenaarFactory, userFactory } from "./user";
88

9-
const FIXTURE_DESTRUCTION_LIST: DestructionList = {
9+
export const FIXTURE_DESTRUCTION_LIST: DestructionList = {
1010
pk: 1,
1111
uuid: "00000000-0000-0000-0000-000000000000",
1212
name: "My First Destruction List",
@@ -22,12 +22,10 @@ const FIXTURE_DESTRUCTION_LIST: DestructionList = {
2222
statusChanged: "2024-07-11:16:57",
2323
};
2424

25-
const destructionListFactory = createObjectFactory<DestructionList>(
25+
export const destructionListFactory = createObjectFactory<DestructionList>(
2626
FIXTURE_DESTRUCTION_LIST,
2727
);
2828

29-
export { destructionListFactory };
30-
3129
const FIXTURE_DESTRUCTION_LIST_ASSIGNEE: DestructionListAssignee = {
3230
user: beoordelaarFactory(),
3331
role: "main_reviewer",
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { userEvent, within } from "@storybook/test";
33

4-
import { ReactRouterDecorator } from "../../../../.storybook/decorators";
4+
import {
5+
ClearSessionStorageDecorator,
6+
ReactRouterDecorator,
7+
} from "../../../../.storybook/decorators";
58
import {
69
assertCheckboxSelection,
710
assertColumnSelection,
11+
clickButton,
12+
fillForm,
813
} from "../../../../.storybook/playFunctions";
914
import { paginatedZakenFactory } from "../../../fixtures/paginatedZaken";
1015
import {
@@ -23,18 +28,7 @@ import { DestructionListCreateContext } from "./DestructionListCreate.loader";
2328
const meta: Meta<typeof DestructionListCreatePage> = {
2429
title: "Pages/DestructionList/DestructionListCreatePage",
2530
component: DestructionListCreatePage,
26-
decorators: [ReactRouterDecorator],
27-
};
28-
29-
export default meta;
30-
type Story = StoryObj<typeof meta>;
31-
32-
const FIXTURE: DestructionListCreateContext = {
33-
reviewers: usersFactory(),
34-
paginatedZaken: paginatedZakenFactory(),
35-
};
36-
37-
export const DestructionListCreatePageStory: Story = {
31+
decorators: [ClearSessionStorageDecorator, ReactRouterDecorator],
3832
parameters: {
3933
reactRouterDecorator: {
4034
route: {
@@ -46,40 +40,39 @@ export const DestructionListCreatePageStory: Story = {
4640
},
4741
},
4842
},
49-
play: async (context) => {
50-
await assertCheckboxSelection(context);
51-
await assertColumnSelection(context);
52-
await assertCheckboxSelection({
53-
...context,
54-
parameters: { direction: "forwards" },
55-
});
43+
};
5644

57-
const canvas = within(context.canvasElement);
58-
const buttonCreate = await canvas.findByRole("button", {
59-
name: "Vernietigingslijst opstellen",
60-
});
45+
export default meta;
46+
type Story = StoryObj<typeof meta>;
6147

62-
await userEvent.click(buttonCreate, { delay: 10 });
48+
const FIXTURE: DestructionListCreateContext = {
49+
reviewers: usersFactory(),
50+
paginatedZaken: paginatedZakenFactory(),
51+
};
6352

64-
const modal = await canvas.findByRole("dialog");
65-
const inputName = await within(modal).findByLabelText("Naam");
66-
await userEvent.type(
67-
inputName,
68-
[recordManagerFactory().firstName, recordManagerFactory().lastName].join(
69-
" ",
70-
),
71-
{ delay: 10 },
72-
);
53+
export const CheckboxSelection: Story = {
54+
play: assertCheckboxSelection,
55+
};
7356

74-
const selectFirstReviewer = await within(modal).findByLabelText("Reviewer");
75-
await userEvent.click(selectFirstReviewer, { delay: 10 });
57+
export const ColumnSelection: Story = {
58+
play: assertColumnSelection,
59+
};
7660

77-
const beoordelaar = beoordelaarFactory();
78-
const selectReviewerBeoordelaarOption = await within(modal).findAllByText(
79-
`${beoordelaar.firstName} ${beoordelaar.lastName} (${beoordelaar.username})`,
80-
);
81-
await userEvent.click(selectReviewerBeoordelaarOption[0], {
82-
delay: 10,
83-
});
61+
export const CreateDestructionList: Story = {
62+
parameters: {
63+
assertCheckboxSelection: { direction: "forwards" },
64+
clickButton: { name: "Vernietigingslijst opstellen" },
65+
fillForm: {
66+
formValues: {
67+
Naam: "My First Destruction List",
68+
Reviewer: "Beoor del Laar (Beoor del Laar)",
69+
Opmerking: "CreateDestructionList",
70+
},
71+
},
72+
},
73+
play: async (context) => {
74+
await assertCheckboxSelection(context);
75+
await clickButton(context);
76+
await fillForm(context);
8477
},
8578
};

0 commit comments

Comments
 (0)