Skip to content

Commit f3c5114

Browse files
committed
Add storybook tests for groups
1 parent b9a817c commit f3c5114

2 files changed

Lines changed: 216 additions & 3 deletions

File tree

packages/smart-forms-renderer/src/stories/sdc/ItemControlGroup.stories.tsx

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import type { Meta, StoryObj } from '@storybook/react-vite';
19+
import { expect } from 'storybook/test';
1920
import BuildFormWrapperForStorybook from '../storybookWrappers/BuildFormWrapperForStorybook';
2021
import {
2122
qItemControlDisplayTabContainer,
@@ -28,6 +29,19 @@ import {
2829
qItemControlGroupPageNonTopLevelPageContainer
2930
} from '../assets/questionnaires';
3031
import { createStory } from '../storybookWrappers/createStory';
32+
import {
33+
clickAddRow,
34+
clickNextPage,
35+
clickNextTab,
36+
clickPreviousPage,
37+
clickPreviousTab,
38+
findByLinkIdOrLabel,
39+
getNthRow,
40+
getGroupAnswers,
41+
inputDate,
42+
inputDecimal,
43+
inputInteger
44+
} from '../testUtils';
3145

3246
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
3347
const meta = {
@@ -45,47 +59,177 @@ type Story = StoryObj<typeof meta>;
4559
export const GTableRepeats: Story = createStory({
4660
args: {
4761
questionnaire: qItemControlGroupGTableRepeats
62+
},
63+
play: async ({ canvasElement }) => {
64+
const groupContainer = await findByLinkIdOrLabel(canvasElement, 'medical-history');
65+
const firstRow = await getNthRow(groupContainer, 0);
66+
await inputDate(firstRow, 'medical-history-onset', '01/04/2026');
67+
68+
await clickAddRow(canvasElement, 'medical-history');
69+
70+
const secondRow = await getNthRow(groupContainer, 1);
71+
await inputDate(secondRow, 'medical-history-onset', '02/04/2026');
72+
73+
const onsetAnswers = await getGroupAnswers('medical-history', 'medical-history-onset');
74+
expect(onsetAnswers).toEqual([
75+
expect.objectContaining({ valueDate: '2026-04-01' }),
76+
expect.objectContaining({ valueDate: '2026-04-02' })
77+
]);
4878
}
4979
}) as Story;
5080

5181
export const GTableSingle: Story = createStory({
5282
args: {
5383
questionnaire: qItemControlGroupGTableSingle
84+
},
85+
play: async ({ canvasElement }) => {
86+
await inputDate(canvasElement, 'medical-history-onset', '15/03/2026');
87+
await inputDate(canvasElement, 'medical-history-recorded', '20/03/2026');
88+
89+
const onsetAnswers = await getGroupAnswers('medical-history', 'medical-history-onset');
90+
const recordedAnswers = await getGroupAnswers('medical-history', 'medical-history-recorded');
91+
expect(onsetAnswers).toEqual([expect.objectContaining({ valueDate: '2026-03-15' })]);
92+
expect(recordedAnswers).toEqual([expect.objectContaining({ valueDate: '2026-03-20' })]);
5493
}
5594
}) as Story;
5695

5796
export const GridSingleRow: Story = createStory({
5897
args: {
5998
questionnaire: qItemControlGroupGridSingleRow
99+
},
100+
play: async ({ canvasElement }) => {
101+
await inputInteger(canvasElement, 'blood-pressure-systolic', 118);
102+
await inputInteger(canvasElement, 'blood-pressure-diastolic', 76);
103+
await inputDate(canvasElement, 'blood-pressure-date', '10/04/2026');
104+
105+
const systolicAnswers = await getGroupAnswers(
106+
'blood-pressure-group',
107+
'blood-pressure-systolic'
108+
);
109+
const diastolicAnswers = await getGroupAnswers(
110+
'blood-pressure-group',
111+
'blood-pressure-diastolic'
112+
);
113+
const dateAnswers = await getGroupAnswers('blood-pressure-group', 'blood-pressure-date');
114+
expect(systolicAnswers).toEqual([expect.objectContaining({ valueInteger: 118 })]);
115+
expect(diastolicAnswers).toEqual([expect.objectContaining({ valueInteger: 76 })]);
116+
expect(dateAnswers).toEqual([expect.objectContaining({ valueDate: '2026-04-10' })]);
60117
}
61118
}) as Story;
62119

63120
export const GridMultiRow: Story = createStory({
64121
args: {
65122
questionnaire: qItemControlGroupGridMultiRow
123+
},
124+
play: async ({ canvasElement }) => {
125+
await inputDecimal(canvasElement, 'height-value', 180.5);
126+
await inputDate(canvasElement, 'height-date-performed', '11/04/2026');
127+
await inputDecimal(canvasElement, 'weight-value', 81.2);
128+
129+
const heightAnswers = await getGroupAnswers('height-row', 'height-value');
130+
const heightDateAnswers = await getGroupAnswers('height-row', 'height-date-performed');
131+
const weightAnswers = await getGroupAnswers('weight-row', 'weight-value');
132+
expect(heightAnswers).toEqual([expect.objectContaining({ valueDecimal: 180.5 })]);
133+
expect(heightDateAnswers).toEqual([expect.objectContaining({ valueDate: '2026-04-11' })]);
134+
expect(weightAnswers).toEqual([expect.objectContaining({ valueDecimal: 81.2 })]);
66135
}
67136
}) as Story;
68137

69138
export const TabContainer: Story = createStory({
70139
args: {
71140
questionnaire: qItemControlDisplayTabContainer
141+
},
142+
play: async ({ canvasElement, step }) => {
143+
await step('Initial tab state is first tab', async () => {
144+
expect(await findByLinkIdOrLabel(canvasElement, 'health-check-eligible')).toBeTruthy();
145+
});
146+
147+
await step('Navigate to next tab', async () => {
148+
await clickNextTab(canvasElement);
149+
expect(
150+
await findByLinkIdOrLabel(canvasElement, 'current-priorities-important-things')
151+
).toBeTruthy();
152+
});
153+
154+
await step('Navigate back to previous tab', async () => {
155+
await clickPreviousTab(canvasElement);
156+
expect(await findByLinkIdOrLabel(canvasElement, 'health-check-eligible')).toBeTruthy();
157+
});
72158
}
73159
}) as Story;
74160

75161
export const Page: Story = createStory({
76162
args: {
77163
questionnaire: qItemControlGroupPage
164+
},
165+
play: async ({ canvasElement, step }) => {
166+
await step('Initial page state is page 1', async () => {
167+
expect(await findByLinkIdOrLabel(canvasElement, '1.1')).toBeTruthy();
168+
});
169+
170+
await step('Navigate to page 2', async () => {
171+
await clickNextPage(canvasElement);
172+
expect(await findByLinkIdOrLabel(canvasElement, '2.1')).toBeTruthy();
173+
});
174+
175+
await step('Navigate back to page 1', async () => {
176+
await clickPreviousPage(canvasElement);
177+
expect(await findByLinkIdOrLabel(canvasElement, '1.1')).toBeTruthy();
178+
});
78179
}
79180
}) as Story;
80181

81182
export const PageContainer: Story = createStory({
82183
args: {
83184
questionnaire: qItemControlGroupPageContainer
185+
},
186+
play: async ({ canvasElement, step }) => {
187+
await step('Initial page-container state is first page', async () => {
188+
expect(await findByLinkIdOrLabel(canvasElement, 'page1')).toBeTruthy();
189+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-header')).toBeTruthy();
190+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-footer')).toBeTruthy();
191+
});
192+
193+
await step('Navigate to second page in page-container', async () => {
194+
await clickNextPage(canvasElement);
195+
expect(await findByLinkIdOrLabel(canvasElement, 'page2')).toBeTruthy();
196+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-header')).toBeTruthy();
197+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-footer')).toBeTruthy();
198+
});
199+
200+
await step('Navigate to third page in page-container', async () => {
201+
await clickNextPage(canvasElement);
202+
expect(await findByLinkIdOrLabel(canvasElement, 'page3')).toBeTruthy();
203+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-header')).toBeTruthy();
204+
expect(await findByLinkIdOrLabel(canvasElement, 'faux-footer')).toBeTruthy();
205+
});
206+
207+
await step('Navigate back to second page in page-container', async () => {
208+
await clickPreviousPage(canvasElement);
209+
expect(await findByLinkIdOrLabel(canvasElement, 'page2')).toBeTruthy();
210+
});
84211
}
85212
}) as Story;
86213

87214
export const PageContainerNonSingleTopLevel: Story = createStory({
88215
args: {
89216
questionnaire: qItemControlGroupPageNonTopLevelPageContainer
217+
},
218+
play: async ({ canvasElement, step }) => {
219+
await step('Initial non-top-level page-container state', async () => {
220+
expect(await findByLinkIdOrLabel(canvasElement, 'group-basic1')).toBeTruthy();
221+
});
222+
223+
await step('Navigate from group-basic1 to group-basic2', async () => {
224+
const groupBasic1 = await findByLinkIdOrLabel(canvasElement, 'group-basic1');
225+
await clickNextPage(groupBasic1);
226+
expect(await findByLinkIdOrLabel(canvasElement, 'group-basic2')).toBeTruthy();
227+
});
228+
229+
await step('Navigate back from group-basic2 to group-basic1', async () => {
230+
const groupBasic2 = await findByLinkIdOrLabel(canvasElement, 'group-basic2');
231+
await clickPreviousPage(groupBasic2);
232+
expect(await findByLinkIdOrLabel(canvasElement, 'group-basic1')).toBeTruthy();
233+
});
90234
}
91235
}) as Story;

packages/smart-forms-renderer/src/stories/testUtils.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ export async function getGroupAnswers(groupLinkId: string, answerLinkId: string)
102102

103103
const result = await evaluate(
104104
qr,
105-
groupLinkId
106-
? `QuestionnaireResponse.item.where(linkId='${groupLinkId}').item.where(linkId='${answerLinkId}').answer`
107-
: `QuestionnaireResponse.item.where(linkId='${answerLinkId}').answer`
105+
`QuestionnaireResponse.repeat(item).where(linkId='${groupLinkId}').item.where(linkId='${answerLinkId}').answer`
108106
);
109107

110108
return result;
@@ -691,6 +689,77 @@ export async function findByLinkIdOrLabel(
691689
);
692690
}
693691

692+
export async function getNthRow(container: HTMLElement, index: number): Promise<HTMLElement> {
693+
return await waitFor(
694+
() => {
695+
const rows = container.querySelectorAll<HTMLElement>('tbody tr');
696+
const row = rows[index];
697+
if (!row) {
698+
throw new Error(`Row at index ${index} not found`);
699+
}
700+
return row;
701+
},
702+
{ timeout: 5000 }
703+
);
704+
}
705+
706+
async function clickNavigationButton(
707+
canvasElement: HTMLElement,
708+
buttonNameMatcher: RegExp,
709+
errorMessage: string
710+
) {
711+
const element = within(canvasElement);
712+
const button = await waitFor(() => {
713+
const navButton = element.queryByRole('button', { name: buttonNameMatcher });
714+
if (!navButton) {
715+
throw new Error(errorMessage);
716+
}
717+
if ((navButton as HTMLButtonElement).disabled) {
718+
throw new Error(`Navigation button is disabled: ${buttonNameMatcher}`);
719+
}
720+
return navButton;
721+
});
722+
723+
await userEvent.click(button);
724+
await new Promise((resolve) => setTimeout(resolve, 300));
725+
}
726+
727+
export async function clickNextTab(canvasElement: HTMLElement) {
728+
await clickNavigationButton(canvasElement, /^next tab$/i, 'Next tab button not found');
729+
}
730+
731+
export async function clickPreviousTab(canvasElement: HTMLElement) {
732+
await clickNavigationButton(canvasElement, /^previous tab$/i, 'Previous tab button not found');
733+
}
734+
735+
export async function clickNextPage(canvasElement: HTMLElement) {
736+
await clickNavigationButton(canvasElement, /^next page$/i, 'Next page button not found');
737+
}
738+
739+
export async function clickPreviousPage(canvasElement: HTMLElement) {
740+
await clickNavigationButton(canvasElement, /^previous page$/i, 'Previous page button not found');
741+
}
742+
743+
export async function clickAddRow(canvasElement: HTMLElement, groupLinkIdOrLabel: string) {
744+
const groupContainer = await findByLinkIdOrLabel(canvasElement, groupLinkIdOrLabel);
745+
746+
const button = await waitFor(() => {
747+
const addButton = groupContainer.querySelector<HTMLButtonElement>(
748+
'button[data-test="button-add-repeat-item"]'
749+
);
750+
if (!addButton) {
751+
throw new Error(`Add row button not found inside group "${groupLinkIdOrLabel}"`);
752+
}
753+
if (addButton.disabled) {
754+
throw new Error(`Add row button is disabled for group "${groupLinkIdOrLabel}"`);
755+
}
756+
return addButton;
757+
});
758+
759+
await userEvent.click(button);
760+
await new Promise((resolve) => setTimeout(resolve, 300));
761+
}
762+
694763
export async function inputOpenChoiceOtherText(
695764
canvasElement: HTMLElement,
696765
linkId: string,

0 commit comments

Comments
 (0)