Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/layout/Summary/SummaryComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ const SummaryComponentInner = React.forwardRef(function (
onChangeClick={onChangeClick}
changeText={langAsString('form_filler.summary_item_change')}
targetBaseComponentId={targetBaseComponentId}
overrides={{ largeGroup, display, pageBreak, grid, excludedChildren }}
// Intentionally not passing the pageBreak override here, as that will cause the property to
// be inherited forever in repeating groups, causing every component inside to be on a separate page.
// Passing `grid` here would make every Summary render use the grid settings for the topmost component
overrides={{ largeGroup, display, excludedChildren }}
RenderSummary={RenderSummary}
/>
) : (
Expand Down
69 changes: 69 additions & 0 deletions test/e2e/integration/frontend-test/pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,75 @@ describe('PDF', () => {
});
});

it('should generate PDF for group step (using Summary1 pdfLayout)', () => {
cy.intercept('GET', '**/api/layoutsettings/group', (req) => {
req.on('response', (res) => {
const body = JSON.parse(res.body) as ILayoutSettings;
body.pages.pdfLayoutName = 'summary'; // Forces PDF engine to use the 'summary' page as the PDF page
res.send(body);
});
}).as('settings');

cy.goto('group');
cy.findByRole('checkbox', { name: /liten/i }).check();
cy.findByRole('checkbox', { name: /middels/i }).check();
cy.findByRole('checkbox', { name: /stor/i }).check();
cy.findByRole('checkbox', { name: /svær/i }).check();
cy.findByRole('checkbox', { name: /enorm/i }).check();

cy.gotoNavPage('repeating');
cy.findByRole('checkbox', { name: /ja/i }).check();

cy.interceptLayout('group', (component) => {
if (component.type === 'RepeatingGroup' && component.id === 'mainGroup') {
component.pageBreak = {
breakBefore: 'always',
breakAfter: 'auto',
};
}
});

cy.testPdf({
freeze: false,
snapshotName: 'group-custom-summary1',
callback: () => {
// Regression test for https://github.com/Altinn/app-frontend-react/issues/3745
cy.expectPageBreaks(6);
},
});
});

it('should generate PDF for group step (using Summary2 automatic PDF)', () => {
cy.setFeatureToggle('betaPDFenabled', true);
cy.goto('group');
cy.findByRole('checkbox', { name: /liten/i }).check();
cy.findByRole('checkbox', { name: /middels/i }).check();
cy.findByRole('checkbox', { name: /stor/i }).check();
cy.findByRole('checkbox', { name: /svær/i }).check();
cy.findByRole('checkbox', { name: /enorm/i }).check();

cy.gotoNavPage('repeating');
cy.findByRole('checkbox', { name: /ja/i }).check();

cy.interceptLayout('group', (component) => {
if (component.type === 'RepeatingGroup' && component.id === 'mainGroup') {
component.pageBreak = {
breakBefore: 'always',
breakAfter: 'auto',
};
}
});

cy.testPdf({
freeze: false,
snapshotName: 'group-custom-summary2',
callback: () => {
// Summary2 doesn't do page-breaks per row, only for the component itself
cy.expectPageBreaks(1);
},
});
});

it('should generate PDF for likert step', () => {
cy.goto('likert');
cy.findByRole('table', { name: likertPage.optionalTableTitle }).within(() => {
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/integration/multiple-datamodels-test/saving.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('saving multiple data models', () => {
// same time.
cy.intercept('PATCH', '**/data*').as('saveFormData');
cy.startAppInstance(appFrontend.apps.multipleDatamodelsTest);
cy.setCookie('FEATURE_saveOnBlur', 'false');
cy.setFeatureToggle('saveOnBlur', false);
});

it('Calls save on individual data models', () => {
Expand Down
46 changes: 35 additions & 11 deletions test/e2e/support/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { ResponseFuzzing, Size, SnapshotOptions, SnapshotViewport } from 't
import { breakpoints } from 'src/hooks/useDeviceWidths';
import { getInstanceIdRegExp } from 'src/utils/instanceIdRegExp';
import type { LayoutContextValue } from 'src/features/form/layout/LayoutsContext';
import type { IFeatureToggles } from 'src/features/toggles';
import type { ILayoutFile } from 'src/layout/common.generated';
import JQueryWithSelector = Cypress.JQueryWithSelector;

Expand Down Expand Up @@ -635,6 +636,7 @@ Cypress.Commands.add(
snapshotName = false,
beforeReload,
callback,
freeze = true,
returnToForm = false,
enableResponseFuzzing = false,
buildUrl = buildPdfUrl,
Expand Down Expand Up @@ -685,19 +687,24 @@ Cypress.Commands.add(
cy.viewport(794, 1123);
cy.get('body').invoke('css', 'margin', '0.75in');

// Stops timers which helps in 'freezing' the page in its current state, makes it easier to see when data is missing
cy.clock();

cy.then(() => {
const timeout = setTimeout(() => {
throw 'PDF callback failed, print was not ready when #readyForPrint appeared';
}, 0);
// Verify that generic elements that should be hidden are not present
if (freeze) {
// Stops timers which helps in 'freezing' the page in its current state, makes it easier to see when data is missing
cy.clock();

cy.then(() => {
const timeout = setTimeout(() => {
throw 'PDF callback failed, print was not ready when #readyForPrint appeared';
}, 0);
// Verify that generic elements that should be hidden are not present
cy.findAllByRole('button').should('not.exist');
// Run tests from callback
callback();
cy.then(() => clearTimeout(timeout));
});
} else {
cy.findAllByRole('button').should('not.exist');
// Run tests from callback
callback();
cy.then(() => clearTimeout(timeout));
});
}

// Disable response fuzzing and re-enable caching
cy.get<ResponseFuzzing>('@responseFuzzing').invoke('disable');
Expand Down Expand Up @@ -987,3 +994,20 @@ Cypress.Commands.add('openNavGroup', (groupName, pageName, subformName) => {
});
}
});

Cypress.Commands.add('expectPageBreaks', (expectedCount: number) => {
cy.window().should((win) => {
if (!win.matchMedia('print').matches) {
throw new Error('expectPageBreaks can only be called when media is in print mode');
}
const allElements = Array.from(win.document.querySelectorAll('*'));
const breakBeforeCount = allElements.filter((e) => win.getComputedStyle(e).breakBefore === 'page').length;
const breakAfterCount = allElements.filter((e) => win.getComputedStyle(e).breakAfter === 'page').length;
const pageCount = breakBeforeCount + breakAfterCount;
expect(pageCount).to.equal(expectedCount);
});
});

Cypress.Commands.add('setFeatureToggle', (toggleName: IFeatureToggles, value: boolean) => {
cy.setCookie(`FEATURE_${toggleName}`, value.toString());
});
12 changes: 12 additions & 0 deletions test/e2e/support/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ConsoleMessage } from 'cypress-fail-on-console-error';

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

import type { IFeatureToggles } from 'src/features/toggles';
import type { BackendValidationIssue, BackendValidationIssuesWithSource } from 'src/features/validation';
import type { ILayoutSets } from 'src/layout/common.generated';
import type { CompExternal, ILayoutCollection, ILayouts } from 'src/layout/layout';
Expand Down Expand Up @@ -33,6 +34,7 @@ export type StartAppInstanceOptions = {
export interface TestPdfOptions {
snapshotName?: string;
beforeReload?: () => void;
freeze?: boolean;
callback: () => void;
returnToForm?: boolean;
enableResponseFuzzing?: boolean;
Expand Down Expand Up @@ -325,6 +327,16 @@ declare global {

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

/**
* Assert the approximate number of pages in a printout by counting CSS break-before and break-after page properties
*/
expectPageBreaks(expectedCount: number): Chainable<null>;

/**
* Set a feature toggle value via cookie
*/
setFeatureToggle(toggleName: IFeatureToggles, value: boolean): Chainable<null>;

ignoreConsoleMessages(consoleMessages: ConsoleMessage[]): Chainable<null>;
}
}
Expand Down