Skip to content

Commit 8ebfe02

Browse files
✅ - test: extend deatruction list detail test
1 parent af2a989 commit 8ebfe02

File tree

2 files changed

+281
-25
lines changed

2 files changed

+281
-25
lines changed

frontend/.storybook/playFunctions.ts

+196-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import { ReactRenderer } from "@storybook/react";
1+
import { contexts } from "@maykin-ui/admin-ui";
2+
import { Parameters, ReactRenderer } from "@storybook/react";
23
import { expect, userEvent, waitFor, within } from "@storybook/test";
34
import { PlayFunction } from "@storybook/types";
45

6+
//
7+
// Assertions
8+
//
9+
510
/**
611
* Selects and deselects the "Identificatie" column and asserts whether it's shown/hidden based on the selected state.
712
* @param canvasElement
@@ -124,3 +129,193 @@ export const assertColumnSelection: PlayFunction<ReactRenderer> = async ({
124129
});
125130
expect(identificatieColumn2).toBeVisible();
126131
};
132+
133+
//
134+
// Utils
135+
//
136+
137+
type ClickElementParameters = Parameters & {
138+
checked?: boolean;
139+
elementIndex?: number;
140+
inTBody?: boolean;
141+
role?: string;
142+
name?: string;
143+
};
144+
145+
/**
146+
* Clicks button at position `elementIndex`, within <tbody> if `inTbody` is truthy.
147+
* @param context
148+
*/
149+
export const clickButton: PlayFunction<ReactRenderer> = async (context) => {
150+
await clickElement({
151+
...context,
152+
parameters: {
153+
...context.parameters,
154+
role: "button",
155+
},
156+
});
157+
};
158+
159+
/**
160+
* Clicks checkbox at position `elementIndex`, within <tbody> if `inTbody` is truthy.
161+
* @param context
162+
*/
163+
export const clickCheckbox: PlayFunction<ReactRenderer> = async (context) => {
164+
await clickElement({
165+
...context,
166+
parameters: {
167+
...context.parameters,
168+
role: "checkbox",
169+
},
170+
});
171+
};
172+
173+
/**
174+
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
175+
* @param context
176+
*/
177+
export const clickElement: PlayFunction<ReactRenderer> = async (context) => {
178+
const {
179+
checked,
180+
elementIndex = 0,
181+
inTBody = false,
182+
role,
183+
name,
184+
} = context.parameters as ClickElementParameters;
185+
186+
console.assert(
187+
role,
188+
'clickElement requires an element role be set using the "role" parameter!',
189+
);
190+
191+
const canvas = within(context.canvasElement);
192+
const rowGroups = await canvas.findAllByRole("rowgroup");
193+
194+
const tbody = rowGroups.find((rg) => {
195+
return rg.tagName === "TBODY";
196+
}) as HTMLTableSectionElement;
197+
198+
const elements = await within(
199+
inTBody ? tbody : context.canvasElement,
200+
// @ts-expect-error - role now set.
201+
).findAllByRole(role, { name });
202+
203+
const element = elements[elementIndex];
204+
205+
const checkedState = (element as HTMLInputElement).checked;
206+
207+
// Normalize state.
208+
if (typeof checked !== "undefined" && checked === checkedState) {
209+
await userEvent.click(element, { delay: 10 });
210+
}
211+
212+
await userEvent.click(element, { delay: 10 });
213+
};
214+
215+
type FillFormParameters = Parameters & {
216+
form?: HTMLFormElement;
217+
formValues?: Record<string, boolean | string>;
218+
submitForm?: boolean;
219+
};
220+
221+
/**
222+
* Fills in `form` with `formValues`, then submits if `submitForm` is truthy.
223+
* @param context
224+
*/
225+
export const fillForm: PlayFunction<ReactRenderer> = async (context) => {
226+
const canvas = within(context.canvasElement);
227+
228+
const {
229+
form = await canvas.findByRole("form"),
230+
formValues = {},
231+
submitForm = true,
232+
} = context.parameters as FillFormParameters;
233+
234+
for (const [name, value] of Object.entries(formValues)) {
235+
const field: HTMLInputElement | HTMLSelectElement =
236+
await within(form).findByLabelText(name);
237+
238+
switch (typeof value) {
239+
case "boolean":
240+
const checkbox = field as HTMLInputElement;
241+
if (checkbox.checked !== value) {
242+
await userEvent.click(checkbox, { delay: 100 });
243+
}
244+
break;
245+
case "string":
246+
if ((field as HTMLSelectElement).options) {
247+
const select = field as HTMLSelectElement;
248+
await userEvent.click(select, { delay: 100 });
249+
const option = (await within(form).findAllByText(value))[0];
250+
await userEvent.click(option, { delay: 100 });
251+
} else {
252+
const input = field as HTMLInputElement;
253+
await userEvent.type(input, value, { delay: 10 });
254+
}
255+
}
256+
}
257+
258+
if (submitForm) {
259+
const buttons = await within(form).findAllByRole("button");
260+
// Assume that last button is submit.
261+
const submit = buttons[buttons.length - 1];
262+
await userEvent.click(submit, { delay: 100 });
263+
}
264+
};
265+
266+
type FillConfirmationFormParameters = ClickElementParameters &
267+
FillFormParameters;
268+
269+
/**
270+
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
271+
* Then fills in dialog form, submits if `submitForm` is truthy.
272+
* @param context
273+
*/
274+
export const fillButtonConfirmationForm: PlayFunction<ReactRenderer> = async (
275+
context,
276+
) => {
277+
await fillConfirmationForm({
278+
...context,
279+
parameters: { ...context.parameters, role: "button" },
280+
});
281+
};
282+
283+
/**
284+
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
285+
* Then fills in dialog form, submits if `submitForm` is truthy.
286+
* @param context
287+
*/
288+
export const fillCheckboxConfirmationForm: PlayFunction<ReactRenderer> = async (
289+
context,
290+
) => {
291+
await fillConfirmationForm({
292+
...context,
293+
parameters: { ...context.parameters, checked: true, role: "checkbox" },
294+
});
295+
};
296+
297+
/**
298+
* Clicks element at position `elementIndex`, within <tbody> if `inTbody` is truthy.
299+
* Then fills in dialog form, submits if `submitForm` is truthy.
300+
* @param context
301+
*/
302+
export const fillConfirmationForm: PlayFunction<ReactRenderer> = async (
303+
context,
304+
) => {
305+
const parameters = context.parameters as FillConfirmationFormParameters;
306+
const _context = { ...context, parameters };
307+
await clickElement(_context);
308+
309+
const canvas = within(context.canvasElement);
310+
const modal = await canvas.findByRole("dialog");
311+
// FIXME: Fix in admin-ui form should be picked up by role.
312+
// const form = await within(modal).findByRole("form", {}, { timeout: 3000 });
313+
314+
await fillForm({
315+
..._context,
316+
parameters: {
317+
...parameters,
318+
form: modal,
319+
},
320+
});
321+
};

frontend/src/pages/destructionlist/detail/DestructionListDetail.stories.tsx

+85-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { userEvent, within } from "@storybook/test";
2+
import { findAllByRole, userEvent, waitFor, within } from "@storybook/test";
33

44
import { ReactRouterDecorator } from "../../../../.storybook/decorators";
55
import {
66
assertCheckboxSelection,
77
assertColumnSelection,
8+
fillButtonConfirmationForm,
9+
fillCheckboxConfirmationForm,
810
} from "../../../../.storybook/playFunctions";
911
import { FIXTURE_DESTRUCTION_LIST } from "../../../fixtures/destructionList";
1012
import { FIXTURE_PAGINATED_ZAKEN } from "../../../fixtures/paginatedZaken";
@@ -15,6 +17,11 @@ import {
1517
FIXTURE_SELECTIELIJSTKLASSE_CHOICES_MAP,
1618
} from "../../../fixtures/selectieLijstKlasseChoices";
1719
import { FIXTURE_USERS } from "../../../fixtures/user";
20+
import { FIXTURE_ZAKEN } from "../../../fixtures/zaak";
21+
import {
22+
clearZaakSelection,
23+
getZaakSelection,
24+
} from "../../../lib/zaakSelection/zaakSelection";
1825
import { DestructionListDetailPage } from "./DestructionListDetail";
1926
import { DestructionListDetailContext } from "./types";
2027

@@ -82,10 +89,15 @@ export const EditDestructionList: Story = {
8289
};
8390

8491
const FIXTURE_PROCESS_REVIEW: DestructionListDetailContext = {
85-
storageKey: "storybook-storage-key",
92+
storageKey: `storybook-storage-key!${meta.title}:ProcessReview`,
8693
destructionList: { ...FIXTURE_DESTRUCTION_LIST, status: "changes_requested" },
8794
reviewers: FIXTURE_USERS,
88-
zaken: FIXTURE_PAGINATED_ZAKEN,
95+
zaken: {
96+
count: FIXTURE_REVIEW_ITEMS.length,
97+
next: null,
98+
previous: null,
99+
results: [],
100+
},
89101
selectableZaken: FIXTURE_PAGINATED_ZAKEN,
90102
zaakSelection: {},
91103
review: FIXTURE_REVIEW,
@@ -97,35 +109,84 @@ export const ProcessReview: Story = {
97109
parameters: {
98110
reactRouterDecorator: {
99111
route: {
100-
loader: async () => FIXTURE_PROCESS_REVIEW,
112+
action: async () => true,
113+
loader: async () => {
114+
const zaakSelection = await getZaakSelection(
115+
`${FIXTURE_PROCESS_REVIEW.storageKey}`,
116+
);
117+
118+
return { ...FIXTURE_PROCESS_REVIEW, zaakSelection };
119+
},
101120
},
102121
},
103122
},
104123
play: async (context) => {
105-
const canvas = within(context.canvasElement);
106-
const checkbox = await canvas.findAllByRole("checkbox");
107-
await userEvent.click(checkbox[0], { delay: 10 });
108-
const modal = await canvas.findByRole("dialog");
124+
await fillCheckboxConfirmationForm({
125+
...context,
126+
parameters: {
127+
elementIndex: 0,
128+
formValues: {
129+
"Aanpassen van selectielijstklasse": true,
130+
Selectielijstklasse: FIXTURE_SELECTIELIJSTKLASSE_CHOICES[0].label,
131+
Reden: "omdat het moet",
132+
},
133+
},
134+
});
109135

110-
const checkboxActie = await within(modal).findByLabelText(
111-
"Aanpassen van selectielijstklasse",
112-
);
113-
await userEvent.click(checkboxActie, { delay: 10 });
136+
await fillCheckboxConfirmationForm({
137+
...context,
138+
parameters: {
139+
elementIndex: 1,
140+
formValues: {
141+
"Aanpassen van selectielijstklasse": true,
142+
Selectielijstklasse: FIXTURE_SELECTIELIJSTKLASSE_CHOICES[1].label,
143+
Reden: "omdat het kan",
144+
},
145+
},
146+
});
114147

115-
const selectSelectielijstKlasse = await within(modal).findByLabelText(
116-
"Selectielijstklasse",
117-
);
118-
await userEvent.click(selectSelectielijstKlasse, { delay: 10 });
148+
await fillCheckboxConfirmationForm({
149+
...context,
150+
parameters: {
151+
elementIndex: 2,
152+
formValues: {
153+
"Aanpassen van selectielijstklasse": true,
154+
Selectielijstklasse: FIXTURE_SELECTIELIJSTKLASSE_CHOICES[2].label,
155+
Reden: "Waarom niet",
156+
},
157+
},
158+
});
119159

120-
const selectSelectielijstKlasseOption = await within(modal).findAllByText(
121-
FIXTURE_SELECTIELIJSTKLASSE_CHOICES[0].label,
122-
);
123-
await userEvent.click(selectSelectielijstKlasseOption[0], {
124-
delay: 10,
160+
await fillButtonConfirmationForm({
161+
...context,
162+
parameters: {
163+
name: "Opnieuw indienen",
164+
formValues: {
165+
Opmerking: "Kan gewoon",
166+
},
167+
submitForm: false,
168+
},
125169
});
126170

127-
const inputReden = await within(modal).findByLabelText("Reden");
128-
await userEvent.type(inputReden, "Omdat het moet", { delay: 10 });
129-
await userEvent.tab();
171+
const canvas = within(context.canvasElement);
172+
await userEvent.keyboard("{Escape}");
173+
174+
const dialog = await canvas.findByRole("dialog");
175+
const close = await within(dialog).findByRole("button", { name: "Close" });
176+
await userEvent.click(close, { delay: 300 });
177+
178+
await waitFor(
179+
async () => {
180+
const mutateButtons = canvas.getAllByRole("button", {
181+
name: "Muteren",
182+
});
183+
await userEvent.click(mutateButtons[1], { delay: 10 });
184+
await canvas.findByRole("dialog");
185+
},
186+
{ timeout: 10000 },
187+
);
188+
189+
// Clean up.
190+
await clearZaakSelection(`${FIXTURE_PROCESS_REVIEW.storageKey}`);
130191
},
131192
};

0 commit comments

Comments
 (0)