Skip to content

Commit cf83076

Browse files
olemartinorgOle Martin Handeland
andauthored
Adding PDF support in subforms (#3637)
* This selector failed during load when used in Subform, rewriting to using layout lookups * Enabling PDF support for an individual subform element * Moving existing PDF tests for subform, as I'm about to add more * Deduplicating some code, adding a test to make sure both automatic and custom PDFs work for a given subform element * This was not needed after all * Revert "This selector failed during load when used in Subform, rewriting to using layout lookups" This reverts commit 45ff79d. --------- Co-authored-by: Ole Martin Handeland <[email protected]>
1 parent ab6117e commit cf83076

File tree

5 files changed

+213
-141
lines changed

5 files changed

+213
-141
lines changed

src/layout/Subform/SubformWrapper.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useTaskStore } from 'src/core/contexts/taskStoreContext';
77
import { Loader } from 'src/core/loading/Loader';
88
import { FormProvider } from 'src/features/form/FormContext';
99
import { useDataTypeFromLayoutSet } from 'src/features/form/layout/LayoutsContext';
10+
import { PDFWrapper } from 'src/features/pdf/PDFWrapper';
1011
import { useNavigationParam } from 'src/hooks/navigation';
1112
import { useNavigatePage } from 'src/hooks/useNavigatePage';
1213
import { ProcessTaskType } from 'src/types';
@@ -24,9 +25,11 @@ export function SubformWrapper({ baseComponentId, children }: PropsWithChildren<
2425

2526
export function SubformForm() {
2627
return (
27-
<PresentationComponent type={ProcessTaskType.Data}>
28-
<Form />
29-
</PresentationComponent>
28+
<PDFWrapper>
29+
<PresentationComponent type={ProcessTaskType.Data}>
30+
<Form />
31+
</PresentationComponent>
32+
</PDFWrapper>
3033
);
3134
}
3235

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { AppFrontend } from 'test/e2e/pageobjects/app-frontend';
2+
3+
import type { ILayoutSettings } from 'src/layout/common.generated';
4+
import type { ILayoutCollection } from 'src/layout/layout';
5+
6+
const appFrontend = new AppFrontend();
7+
8+
function fillTwoSubforms() {
9+
cy.findByRole('textbox', { name: /navn/i }).type('Per');
10+
cy.findByRole('textbox', { name: /alder/i }).type('28');
11+
12+
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
13+
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('ABC123');
14+
cy.findByRole('textbox', { name: /merke/i }).type('Digdir');
15+
cy.findByRole('textbox', { name: /modell/i }).type('Scooter2000');
16+
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2024');
17+
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
18+
19+
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
20+
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('XYZ987');
21+
cy.findByRole('textbox', { name: /merke/i }).type('Altinn');
22+
cy.findByRole('textbox', { name: /modell/i }).type('3.0');
23+
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2030');
24+
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
25+
}
26+
27+
describe('Subform test', () => {
28+
beforeEach(() => {
29+
cy.startAppInstance(appFrontend.apps.subformTest, { authenticationLevel: '1' });
30+
});
31+
32+
it('PDF should include subforms + single-subform PDFs should work', () => {
33+
fillTwoSubforms();
34+
cy.testPdf({
35+
snapshotName: 'subform',
36+
enableResponseFuzzing: true,
37+
returnToForm: true,
38+
callback: () => {
39+
cy.getSummary('Navn').should('contain.text', 'Per');
40+
cy.getSummary('Alder').should('contain.text', '28 år');
41+
42+
cy.getSummary('Registreringsnummer').eq(0).should('contain.text', 'ABC123');
43+
cy.getSummary('Merke').eq(0).should('contain.text', 'Digdir');
44+
cy.getSummary('Modell').eq(0).should('contain.text', 'Scooter2000');
45+
cy.getSummary('Produksjonsår').eq(0).should('contain.text', '2024');
46+
47+
cy.getSummary('Registreringsnummer').eq(1).should('contain.text', 'XYZ987');
48+
cy.getSummary('Merke').eq(1).should('contain.text', 'Altinn');
49+
cy.getSummary('Modell').eq(1).should('contain.text', '3.0');
50+
cy.getSummary('Produksjonsår').eq(1).should('contain.text', '2030');
51+
},
52+
});
53+
54+
cy.findAllByRole('button', { name: /endre/i }).first().clickAndGone();
55+
cy.findByRole('textbox', { name: /registreringsnummer/i }).should('have.value', 'ABC123');
56+
57+
cy.testPdf({
58+
snapshotName: 'single-subform',
59+
enableResponseFuzzing: true,
60+
returnToForm: true,
61+
buildUrl: buildUrlForSingleSubform,
62+
callback: () => {
63+
cy.get('#moped-blurb').should(
64+
'contain.text',
65+
'Fyll inn dette skjemaet for å registrere en moped i din garasje',
66+
);
67+
cy.getSummary('Navn').should('not.exist');
68+
cy.getSummary('Alder').should('not.exist');
69+
70+
cy.getSummary('Registreringsnummer').should('contain.text', 'ABC123');
71+
cy.getSummary('Registreringsnummer').should('not.contain.text', 'XYZ987');
72+
cy.getSummary('Merke').should('contain.text', 'Digdir');
73+
cy.getSummary('Modell').should('contain.text', 'Scooter2000');
74+
cy.getSummary('Produksjonsår').should('contain.text', '2024');
75+
},
76+
});
77+
78+
cy.intercept('GET', '**/api/layoutsettings/moped-subform', (req) => {
79+
req.on('response', (res) => {
80+
const body = JSON.parse(res.body) as ILayoutSettings;
81+
body.pages.pdfLayoutName = 'moped-pdf'; // Forces PDF engine to use a tailor-made layout
82+
res.send(body);
83+
});
84+
}).as('settings');
85+
86+
cy.testPdf({
87+
snapshotName: 'single-subform-custom',
88+
enableResponseFuzzing: true,
89+
buildUrl: buildUrlForSingleSubform,
90+
callback: () => {
91+
cy.get('#moped-blurb').should('not.exist');
92+
cy.getSummary('Navn').should('not.exist');
93+
cy.getSummary('Alder').should('not.exist');
94+
95+
cy.getSummary('Registreringsnummer').should('contain.text', 'ABC123');
96+
cy.getSummary('Registreringsnummer').should('not.contain.text', 'XYZ987');
97+
cy.getSummary('Merke').should('contain.text', 'Digdir');
98+
cy.getSummary('Modell').should('contain.text', 'Scooter2000');
99+
cy.getSummary('Produksjonsår').should('contain.text', '2024');
100+
},
101+
});
102+
});
103+
104+
it('should render PDF with summary2 layoutset with subform and subform table', () => {
105+
const pdfLayoutName = 'CustomPDF';
106+
cy.intercept('GET', '**/layoutsettings/**', (req) =>
107+
req.on('response', (res) => {
108+
const body: ILayoutSettings = JSON.parse(res.body);
109+
res.send({
110+
...body,
111+
pages: { ...body.pages, pdfLayoutName },
112+
});
113+
}),
114+
);
115+
116+
cy.intercept('GET', '**/layouts/**', (req) =>
117+
req.on('response', (res) => {
118+
const body: ILayoutCollection = JSON.parse(res.body);
119+
res.send({
120+
...body,
121+
[pdfLayoutName]: {
122+
data: {
123+
layout: [
124+
{
125+
id: 'title',
126+
type: 'Header',
127+
textResourceBindings: { title: 'This is a custom PDF' },
128+
size: 'L',
129+
},
130+
{
131+
id: 'summary2-layoutset',
132+
type: 'Summary2',
133+
target: {
134+
taskId: 'Task_1',
135+
type: 'layoutSet',
136+
},
137+
showPageInAccordion: false,
138+
overrides: [
139+
{
140+
componentId: 'subform-mopeder',
141+
display: 'table',
142+
},
143+
],
144+
},
145+
],
146+
},
147+
},
148+
});
149+
}),
150+
);
151+
152+
cy.waitUntilSaved();
153+
fillTwoSubforms();
154+
155+
cy.testPdf({
156+
snapshotName: 'subform',
157+
enableResponseFuzzing: true,
158+
callback: () => {
159+
cy.getSummary('Navn').should('contain.text', 'Per');
160+
cy.getSummary('Alder').should('contain.text', '28 år');
161+
162+
cy.findByRole('columnheader', { name: 'Regnummer' });
163+
cy.findByRole('columnheader', { name: 'Merke' });
164+
cy.findByRole('columnheader', { name: 'Ekstra info' });
165+
166+
cy.findByRole('cell', { name: 'ABC123' });
167+
cy.findByRole('cell', { name: 'Digdir' });
168+
cy.findByRole('cell', { name: 'XYZ987' });
169+
cy.findByRole('cell', { name: 'Altinn' });
170+
},
171+
});
172+
});
173+
});
174+
175+
function buildUrlForSingleSubform(href: string) {
176+
if (!href.includes('/utfylling/') && !href.includes('/whatever/')) {
177+
// We replace this with 'whatever' to make sure we can still load the PDF whatever the main page
178+
// name is. It should not matter inside this subform.
179+
throw new Error('Expected URL to contain /utfylling/ but it was not found');
180+
}
181+
182+
if (!href.endsWith('/moped-utfylling')) {
183+
throw new Error('Expected URL to end with /moped-utfylling but it was not found');
184+
}
185+
186+
return href.replace('/utfylling/', '/whatever/').replace('/moped-utfylling', '/?pdf=1');
187+
}

test/e2e/integration/subform-test/subform.ts

Lines changed: 0 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { AppFrontend } from 'test/e2e/pageobjects/app-frontend';
22

3-
import type { ILayoutSettings } from 'src/layout/common.generated';
4-
import type { ILayoutCollection } from 'src/layout/layout';
5-
63
const appFrontend = new AppFrontend();
74

85
describe('Subform test', () => {
@@ -222,128 +219,4 @@ describe('Subform test', () => {
222219
cy.findByRole('button', { name: /neste/i }).should('be.visible');
223220
cy.get(appFrontend.errorReport).should('not.exist');
224221
});
225-
226-
it('PDF should include subforms', () => {
227-
cy.findByRole('textbox', { name: /navn/i }).type('Per');
228-
cy.findByRole('textbox', { name: /alder/i }).type('28');
229-
230-
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
231-
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('ABC123');
232-
cy.findByRole('textbox', { name: /merke/i }).type('Digdir');
233-
cy.findByRole('textbox', { name: /modell/i }).type('Scooter2000');
234-
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2024');
235-
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
236-
237-
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
238-
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('XYZ987');
239-
cy.findByRole('textbox', { name: /merke/i }).type('Altinn');
240-
cy.findByRole('textbox', { name: /modell/i }).type('3.0');
241-
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2030');
242-
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
243-
244-
cy.testPdf({
245-
snapshotName: 'subform',
246-
enableResponseFuzzing: true,
247-
callback: () => {
248-
cy.getSummary('Navn').should('contain.text', 'Per');
249-
cy.getSummary('Alder').should('contain.text', '28 år');
250-
251-
cy.getSummary('Registreringsnummer').eq(0).should('contain.text', 'ABC123');
252-
cy.getSummary('Merke').eq(0).should('contain.text', 'Digdir');
253-
cy.getSummary('Modell').eq(0).should('contain.text', 'Scooter2000');
254-
cy.getSummary('Produksjonsår').eq(0).should('contain.text', '2024');
255-
256-
cy.getSummary('Registreringsnummer').eq(1).should('contain.text', 'XYZ987');
257-
cy.getSummary('Merke').eq(1).should('contain.text', 'Altinn');
258-
cy.getSummary('Modell').eq(1).should('contain.text', '3.0');
259-
cy.getSummary('Produksjonsår').eq(1).should('contain.text', '2030');
260-
},
261-
});
262-
});
263-
264-
it('should render PDF with summary2 layoutset with subform and subform table', () => {
265-
const pdfLayoutName = 'CustomPDF';
266-
cy.intercept('GET', '**/layoutsettings/**', (req) =>
267-
req.on('response', (res) => {
268-
const body: ILayoutSettings = JSON.parse(res.body);
269-
res.send({
270-
...body,
271-
pages: { ...body.pages, pdfLayoutName },
272-
});
273-
}),
274-
);
275-
276-
cy.intercept('GET', '**/layouts/**', (req) =>
277-
req.on('response', (res) => {
278-
const body: ILayoutCollection = JSON.parse(res.body);
279-
res.send({
280-
...body,
281-
[pdfLayoutName]: {
282-
data: {
283-
layout: [
284-
{
285-
id: 'title',
286-
type: 'Header',
287-
textResourceBindings: { title: 'This is a custom PDF' },
288-
size: 'L',
289-
},
290-
{
291-
id: 'summary2-layoutset',
292-
type: 'Summary2',
293-
target: {
294-
taskId: 'Task_1',
295-
type: 'layoutSet',
296-
},
297-
showPageInAccordion: false,
298-
overrides: [
299-
{
300-
componentId: 'subform-mopeder',
301-
display: 'table',
302-
},
303-
],
304-
},
305-
],
306-
},
307-
},
308-
});
309-
}),
310-
);
311-
312-
cy.waitUntilSaved();
313-
314-
cy.findByRole('textbox', { name: /navn/i }).type('Per');
315-
cy.findByRole('textbox', { name: /alder/i }).type('28');
316-
317-
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
318-
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('ABC123');
319-
cy.findByRole('textbox', { name: /merke/i }).type('Digdir');
320-
cy.findByRole('textbox', { name: /modell/i }).type('Scooter2000');
321-
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2024');
322-
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
323-
324-
cy.findByRole('button', { name: /legg til moped/i }).clickAndGone();
325-
cy.findByRole('textbox', { name: /registreringsnummer/i }).type('XYZ987');
326-
cy.findByRole('textbox', { name: /merke/i }).type('Altinn');
327-
cy.findByRole('textbox', { name: /modell/i }).type('3.0');
328-
cy.findByRole('textbox', { name: /produksjonsår/i }).type('2030');
329-
cy.findByRole('button', { name: /ferdig/i }).clickAndGone();
330-
331-
cy.testPdf({
332-
snapshotName: 'subform',
333-
enableResponseFuzzing: true,
334-
callback: () => {
335-
cy.getSummary('Navn').should('contain.text', 'Per');
336-
cy.getSummary('Alder').should('contain.text', '28 år');
337-
338-
cy.findByRole('columnheader', { name: 'Regnummer' });
339-
cy.findByRole('columnheader', { name: 'Merke' });
340-
cy.findByRole('columnheader', { name: 'Ekstra info' });
341-
342-
cy.findByRole('cell', { name: 'ABC123' });
343-
cy.findByRole('cell', { name: 'Digdir' });
344-
cy.findByRole('cell', { name: 'XYZ987' });
345-
cy.findByRole('cell', { name: 'Altinn' });
346-
},
347-
});
348-
});
349222
});

0 commit comments

Comments
 (0)