Skip to content

Commit 831824d

Browse files
olemartinorgOle Martin Handeland
andauthored
Fix for pageBreak bug in repeating groups (#3746)
* Fixing pagebreak bug and adding regression test * Fixing tests, adding one for Summary2 --------- Co-authored-by: Ole Martin Handeland <[email protected]>
1 parent 2e6c896 commit 831824d

File tree

5 files changed

+121
-13
lines changed

5 files changed

+121
-13
lines changed

src/layout/Summary/SummaryComponent.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,10 @@ const SummaryComponentInner = React.forwardRef(function (
189189
onChangeClick={onChangeClick}
190190
changeText={langAsString('form_filler.summary_item_change')}
191191
targetBaseComponentId={targetBaseComponentId}
192-
overrides={{ largeGroup, display, pageBreak, grid, excludedChildren }}
192+
// Intentionally not passing the pageBreak override here, as that will cause the property to
193+
// be inherited forever in repeating groups, causing every component inside to be on a separate page.
194+
// Passing `grid` here would make every Summary render use the grid settings for the topmost component
195+
overrides={{ largeGroup, display, excludedChildren }}
193196
RenderSummary={RenderSummary}
194197
/>
195198
) : (

test/e2e/integration/frontend-test/pdf.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,75 @@ describe('PDF', () => {
230230
});
231231
});
232232

233+
it('should generate PDF for group step (using Summary1 pdfLayout)', () => {
234+
cy.intercept('GET', '**/api/layoutsettings/group', (req) => {
235+
req.on('response', (res) => {
236+
const body = JSON.parse(res.body) as ILayoutSettings;
237+
body.pages.pdfLayoutName = 'summary'; // Forces PDF engine to use the 'summary' page as the PDF page
238+
res.send(body);
239+
});
240+
}).as('settings');
241+
242+
cy.goto('group');
243+
cy.findByRole('checkbox', { name: /liten/i }).check();
244+
cy.findByRole('checkbox', { name: /middels/i }).check();
245+
cy.findByRole('checkbox', { name: /stor/i }).check();
246+
cy.findByRole('checkbox', { name: /svær/i }).check();
247+
cy.findByRole('checkbox', { name: /enorm/i }).check();
248+
249+
cy.gotoNavPage('repeating');
250+
cy.findByRole('checkbox', { name: /ja/i }).check();
251+
252+
cy.interceptLayout('group', (component) => {
253+
if (component.type === 'RepeatingGroup' && component.id === 'mainGroup') {
254+
component.pageBreak = {
255+
breakBefore: 'always',
256+
breakAfter: 'auto',
257+
};
258+
}
259+
});
260+
261+
cy.testPdf({
262+
freeze: false,
263+
snapshotName: 'group-custom-summary1',
264+
callback: () => {
265+
// Regression test for https://github.com/Altinn/app-frontend-react/issues/3745
266+
cy.expectPageBreaks(6);
267+
},
268+
});
269+
});
270+
271+
it('should generate PDF for group step (using Summary2 automatic PDF)', () => {
272+
cy.setFeatureToggle('betaPDFenabled', true);
273+
cy.goto('group');
274+
cy.findByRole('checkbox', { name: /liten/i }).check();
275+
cy.findByRole('checkbox', { name: /middels/i }).check();
276+
cy.findByRole('checkbox', { name: /stor/i }).check();
277+
cy.findByRole('checkbox', { name: /svær/i }).check();
278+
cy.findByRole('checkbox', { name: /enorm/i }).check();
279+
280+
cy.gotoNavPage('repeating');
281+
cy.findByRole('checkbox', { name: /ja/i }).check();
282+
283+
cy.interceptLayout('group', (component) => {
284+
if (component.type === 'RepeatingGroup' && component.id === 'mainGroup') {
285+
component.pageBreak = {
286+
breakBefore: 'always',
287+
breakAfter: 'auto',
288+
};
289+
}
290+
});
291+
292+
cy.testPdf({
293+
freeze: false,
294+
snapshotName: 'group-custom-summary2',
295+
callback: () => {
296+
// Summary2 doesn't do page-breaks per row, only for the component itself
297+
cy.expectPageBreaks(1);
298+
},
299+
});
300+
});
301+
233302
it('should generate PDF for likert step', () => {
234303
cy.goto('likert');
235304
cy.findByRole('table', { name: likertPage.optionalTableTitle }).within(() => {

test/e2e/integration/multiple-datamodels-test/saving.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('saving multiple data models', () => {
1111
// same time.
1212
cy.intercept('PATCH', '**/data*').as('saveFormData');
1313
cy.startAppInstance(appFrontend.apps.multipleDatamodelsTest);
14-
cy.setCookie('FEATURE_saveOnBlur', 'false');
14+
cy.setFeatureToggle('saveOnBlur', false);
1515
});
1616

1717
it('Calls save on individual data models', () => {

test/e2e/support/custom.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ResponseFuzzing, Size, SnapshotOptions, SnapshotViewport } from 't
1212
import { breakpoints } from 'src/hooks/useDeviceWidths';
1313
import { getInstanceIdRegExp } from 'src/utils/instanceIdRegExp';
1414
import type { LayoutContextValue } from 'src/features/form/layout/LayoutsContext';
15+
import type { IFeatureToggles } from 'src/features/toggles';
1516
import type { ILayoutFile } from 'src/layout/common.generated';
1617
import JQueryWithSelector = Cypress.JQueryWithSelector;
1718

@@ -635,6 +636,7 @@ Cypress.Commands.add(
635636
snapshotName = false,
636637
beforeReload,
637638
callback,
639+
freeze = true,
638640
returnToForm = false,
639641
enableResponseFuzzing = false,
640642
buildUrl = buildPdfUrl,
@@ -685,19 +687,24 @@ Cypress.Commands.add(
685687
cy.viewport(794, 1123);
686688
cy.get('body').invoke('css', 'margin', '0.75in');
687689

688-
// Stops timers which helps in 'freezing' the page in its current state, makes it easier to see when data is missing
689-
cy.clock();
690-
691-
cy.then(() => {
692-
const timeout = setTimeout(() => {
693-
throw 'PDF callback failed, print was not ready when #readyForPrint appeared';
694-
}, 0);
695-
// Verify that generic elements that should be hidden are not present
690+
if (freeze) {
691+
// Stops timers which helps in 'freezing' the page in its current state, makes it easier to see when data is missing
692+
cy.clock();
693+
694+
cy.then(() => {
695+
const timeout = setTimeout(() => {
696+
throw 'PDF callback failed, print was not ready when #readyForPrint appeared';
697+
}, 0);
698+
// Verify that generic elements that should be hidden are not present
699+
cy.findAllByRole('button').should('not.exist');
700+
// Run tests from callback
701+
callback();
702+
cy.then(() => clearTimeout(timeout));
703+
});
704+
} else {
696705
cy.findAllByRole('button').should('not.exist');
697-
// Run tests from callback
698706
callback();
699-
cy.then(() => clearTimeout(timeout));
700-
});
707+
}
701708

702709
// Disable response fuzzing and re-enable caching
703710
cy.get<ResponseFuzzing>('@responseFuzzing').invoke('disable');
@@ -987,3 +994,20 @@ Cypress.Commands.add('openNavGroup', (groupName, pageName, subformName) => {
987994
});
988995
}
989996
});
997+
998+
Cypress.Commands.add('expectPageBreaks', (expectedCount: number) => {
999+
cy.window().should((win) => {
1000+
if (!win.matchMedia('print').matches) {
1001+
throw new Error('expectPageBreaks can only be called when media is in print mode');
1002+
}
1003+
const allElements = Array.from(win.document.querySelectorAll('*'));
1004+
const breakBeforeCount = allElements.filter((e) => win.getComputedStyle(e).breakBefore === 'page').length;
1005+
const breakAfterCount = allElements.filter((e) => win.getComputedStyle(e).breakAfter === 'page').length;
1006+
const pageCount = breakBeforeCount + breakAfterCount;
1007+
expect(pageCount).to.equal(expectedCount);
1008+
});
1009+
});
1010+
1011+
Cypress.Commands.add('setFeatureToggle', (toggleName: IFeatureToggles, value: boolean) => {
1012+
cy.setCookie(`FEATURE_${toggleName}`, value.toString());
1013+
});

test/e2e/support/global.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ConsoleMessage } from 'cypress-fail-on-console-error';
44

55
import type { CyUser, TenorUser } from 'test/e2e/support/auth';
66

7+
import type { IFeatureToggles } from 'src/features/toggles';
78
import type { BackendValidationIssue, BackendValidationIssuesWithSource } from 'src/features/validation';
89
import type { ILayoutSets } from 'src/layout/common.generated';
910
import type { CompExternal, ILayoutCollection, ILayouts } from 'src/layout/layout';
@@ -33,6 +34,7 @@ export type StartAppInstanceOptions = {
3334
export interface TestPdfOptions {
3435
snapshotName?: string;
3536
beforeReload?: () => void;
37+
freeze?: boolean;
3638
callback: () => void;
3739
returnToForm?: boolean;
3840
enableResponseFuzzing?: boolean;
@@ -325,6 +327,16 @@ declare global {
325327

326328
openNavGroup(groupName: RegExp, pageName?: RegExp, subformName?: RegExp): Chainable<null>;
327329

330+
/**
331+
* Assert the approximate number of pages in a printout by counting CSS break-before and break-after page properties
332+
*/
333+
expectPageBreaks(expectedCount: number): Chainable<null>;
334+
335+
/**
336+
* Set a feature toggle value via cookie
337+
*/
338+
setFeatureToggle(toggleName: IFeatureToggles, value: boolean): Chainable<null>;
339+
328340
ignoreConsoleMessages(consoleMessages: ConsoleMessage[]): Chainable<null>;
329341
}
330342
}

0 commit comments

Comments
 (0)