Skip to content

Commit 7a9f2b4

Browse files
authored
test(e2e): restore report generation and fix location of received images (#3145)
When an test failure occurred, the report was not generated and the development server did not stop at the end of the test execution. This was due to our custom function `toMatchImageSnapshot` becoming asynchronous in commit bffd25b. The original function is synchronous, so the custom function must be too. In addition to this fix, the logic of the code managing image attachments has been improved, mainly to better separate responsibilities. Storing the expected image copy and calculating the image location are specific to j-i-s, so there's no need to do this. to j-i-s, and are therefore performed first. The function for attaching images to the report only uses the previously calculated image location and is unaware that j-i-s is being used. For tests that redefined the location of output images (usually in a subfolder), the path to the received images was not updated accordingly. It was stored in the same location for each test, so only one file was kept and not stored with the diff and the expected image. Also regenerate the image for the “subprocess.03.collapsed.with.elements” test. - Previously, the test failed when run with firefox and this was for good reasons that were hidden with other browsers due to the threshold value used. - The sub-process on the top left is a "collapsed adhoc sub-process", so 2 markers must be displayed with a space between them. Currently, the adhoc marker is not displayed, but the expand marker is correctly positioned (not centered, but on the left) as if the adhoc marker were displayed. - Commit c0c50aa removed additional spacing between markers, so the expand marker has moved to the right. But the reference snapshot was not updated accordingly. - The test probably accepted a minor visual change like this, but at some point the new version of Firefox added other changes that are above the threshold, and the error now appears. - The problem didn't occur on macOS because the threshold was increased in commit bfa02fc, but this hid the problem.
1 parent be46a70 commit 7a9f2b4

7 files changed

+159
-95
lines changed

test/config/jest.image.ts

+43-22
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { toMatchImageSnapshot } from 'jest-image-snapshot';
2525
import MatcherContext = jest.MatcherContext;
2626
import CustomMatcherResult = jest.CustomMatcherResult;
2727

28-
const jestLog = debugLogger('bv:test:jest:img');
28+
const log = debugLogger('bv:test:jest:img');
2929
const toMatchImageSnapshotWithRealSignature = toMatchImageSnapshot as (received: unknown, options?: MatchImageSnapshotOptions) => CustomMatcherResult;
3030

3131
// The path is relative from the jest-html-reporters page to the folder storing the images
@@ -59,78 +59,99 @@ class RetriesCounter {
5959

6060
const retriesCounter = new RetriesCounter();
6161

62-
async function saveAndRegisterImages(matcherContext: MatcherContext, options: MatchImageSnapshotOptions): Promise<void> {
63-
const snapshotIdentifier = options.customSnapshotIdentifier as string;
64-
// Manage expected and received images
65-
const baseImagePathWithName = `${options.customDiffDir}/${snapshotIdentifier}`;
66-
const expectedImagePath = `${baseImagePathWithName}-expected.png`;
67-
copyFileSync(`${options.customSnapshotsDir}/${snapshotIdentifier}.png`, expectedImagePath);
68-
// this image is generated by jest-image-snapshot when activating `storeReceivedOnFailure`
69-
const receivedImagePath = `${baseImagePathWithName}-received.png`;
70-
62+
async function attachImagesForReport(images: LocationOfImagesForTestReport, matcherContext: MatcherContext): Promise<void> {
7163
// Attach the images to jest-html-reports
7264
// Chain the calls to preserve the attachment order
7365
// Create a custom context as the async call can be done whereas the global jest context has changed (another test is currently running).
7466
// So the test name and path changed, and the images would be attached to the wrong test.
7567
// For the context object structure, see https://github.com/Hazyzh/jest-html-reporters/blob/v3.0.5/helper.ts#L95
7668
const context: Record<symbol, unknown> = {};
69+
const currentTestName = matcherContext.currentTestName;
70+
log('Attaching images to report for test %s', currentTestName);
7771
context[Symbol('bpmn-visualization')] = {
7872
state: {
79-
currentTestName: matcherContext.currentTestName,
73+
currentTestName,
8074
testPath: matcherContext.testPath,
8175
},
8276
matchers: {}, // required by the jest-html-reporters getJestGlobalData function even if not used
8377
};
8478

8579
try {
8680
await addAttach({
87-
attach: computeRelativePathFromReportToSnapshots(`${baseImagePathWithName}-diff.png`),
81+
attach: computeRelativePathFromReportToSnapshots(images.diff),
8882
description: 'diff',
8983
bufferFormat: 'png',
9084
context,
9185
});
9286

9387
await addAttach({
94-
attach: computeRelativePathFromReportToSnapshots(expectedImagePath),
88+
attach: computeRelativePathFromReportToSnapshots(images.expected),
9589
description: 'expected',
9690
bufferFormat: 'png',
9791
context,
9892
});
9993

10094
await addAttach({
101-
attach: computeRelativePathFromReportToSnapshots(receivedImagePath),
95+
attach: computeRelativePathFromReportToSnapshots(images.received),
10296
description: 'received',
10397
bufferFormat: 'png',
10498
context,
10599
});
100+
101+
log('Images attached to report for test %s', currentTestName);
106102
} catch (error) {
107103
console.error(
108-
`Error while attaching images to test ${snapshotIdentifier}.` +
104+
`Error while attaching images to test ${currentTestName}.` +
109105
`The 'jest-html-reporters' reporter is probably not in use. For instance, this occurs when running tests with the IntelliJ/Webstorm Jest runner.`,
110106
error,
111107
);
112108
}
113109
}
114110

111+
type LocationOfImagesForTestReport = {
112+
diff: string;
113+
expected: string;
114+
received: string;
115+
};
116+
117+
function saveImages(options: MatchImageSnapshotOptions): LocationOfImagesForTestReport {
118+
const snapshotIdentifier = options.customSnapshotIdentifier as string;
119+
const baseImagePathWithName = `${options.customDiffDir}/${snapshotIdentifier}`;
120+
121+
const diffImagePath = `${baseImagePathWithName}-diff.png`;
122+
const expectedImagePath = `${baseImagePathWithName}-expected.png`;
123+
copyFileSync(`${options.customSnapshotsDir}/${snapshotIdentifier}.png`, expectedImagePath);
124+
// this image is generated by jest-image-snapshot when activating `storeReceivedOnFailure`
125+
const receivedImagePath = `${options.customReceivedDir}/${snapshotIdentifier}-received.png`;
126+
127+
return {
128+
diff: diffImagePath,
129+
expected: expectedImagePath,
130+
received: receivedImagePath,
131+
};
132+
}
133+
115134
// Improve jest-image-snapshot outputs to facilitate debug
116135
// The 'options' parameter is mandatory for us, and some properties must be set as well
117136
// All options properties used here are always set in bpmn-visualization tests
118137
// If the following implementation would be done directly in jest-image-snapshot, this won't be required as it set default values we cannot access here
119-
async function toMatchImageSnapshotCustom(this: MatcherContext, received: Buffer, options: MatchImageSnapshotOptions): Promise<CustomMatcherResult> {
138+
function toMatchImageSnapshotCustom(this: MatcherContext, received: Buffer, options: MatchImageSnapshotOptions): CustomMatcherResult {
120139
const testId = this.currentTestName;
121140
retriesCounter.incrementExecutionCount(testId);
122-
jestLog("Test: '%s' (test file path: '%s')", this.currentTestName, this.testPath);
141+
log("Test: '%s' (test file path: '%s')", this.currentTestName, this.testPath);
123142
const executionCount = retriesCounter.getExecutionCount(testId);
124-
jestLog('Ready to execute toMatchImageSnapshot, execution count: %s', executionCount);
143+
log('Ready to execute toMatchImageSnapshot, execution count: %s', executionCount);
125144
const result = toMatchImageSnapshotWithRealSignature.call(this, received, options);
126-
jestLog('toMatchImageSnapshot executed');
145+
log('toMatchImageSnapshot executed');
127146

128147
if (result.pass) {
129-
jestLog('Result: success');
148+
log('Result: success');
130149
} else {
131-
jestLog('Result: failure');
150+
log('Result: failure');
132151
if (retriesCounter.hasReachMaxRetries(testId)) {
133-
await saveAndRegisterImages(this, options);
152+
const imageLocationInformation = saveImages(options);
153+
log('Images saved and ready to be registered to the report', imageLocationInformation);
154+
attachImagesForReport(imageLocationInformation, this).catch(error => log('Fail to attach images to the report %s', error));
134155
}
135156

136157
// Add configured failure threshold in the error message
Loading

test/e2e/bpmn.rendering.test.ts

-6
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,6 @@ class ImageSnapshotThresholds extends MultiBrowserImageSnapshotThresholds {
165165
windows: 0.66 / 100, // 0.6566433292574891%
166166
},
167167
],
168-
[
169-
'subprocess.03.collapsed.with.elements',
170-
{
171-
macos: 0.11 / 100, // 0.10203186226079852%
172-
},
173-
],
174168
]);
175169
}
176170

test/e2e/diagram.navigation.fit.test.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import type { MatchImageSnapshotOptions } from 'jest-image-snapshot';
1919

2020
import path from 'node:path';
2121

22-
import 'jest-playwright-preset';
22+
import { ImageSnapshotConfigurator, MultiBrowserImageSnapshotThresholds, withCustomOutputDirectory } from './helpers/visu/image-snapshot-config';
2323

24-
import { ImageSnapshotConfigurator, MultiBrowserImageSnapshotThresholds } from './helpers/visu/image-snapshot-config';
24+
import 'jest-playwright-preset';
2525

2626
import { FitType } from '@lib/component/options';
2727
import { AvailableTestPages, PageTester } from '@test/shared/visu/bpmn-page-utils';
@@ -34,10 +34,13 @@ class FitImageSnapshotConfigurator extends ImageSnapshotConfigurator {
3434
fitType: FitType;
3535
margin?: number;
3636
}): MatchImageSnapshotOptions {
37-
const config = super.getConfig(parameter);
38-
config.customSnapshotsDir = FitImageSnapshotConfigurator.buildSnapshotFitDirectory(config.customSnapshotsDir, parameter.fitType, true, parameter.margin ?? 0);
39-
config.customDiffDir = parameter.buildCustomDiffDir(config, parameter.fitType, parameter.margin);
40-
return config;
37+
return withCustomOutputDirectory(
38+
{
39+
...super.getConfig(parameter),
40+
customSnapshotsDir: FitImageSnapshotConfigurator.buildSnapshotFitDirectory(super.getConfig(parameter).customSnapshotsDir, parameter.fitType, true, parameter.margin ?? 0),
41+
},
42+
parameter.buildCustomDiffDir(super.getConfig(parameter), parameter.fitType, parameter.margin),
43+
);
4144
}
4245

4346
private static buildSnapshotFitDirectory(parentDirectory: string, fitType: FitType, withMargin = false, margin?: number): string {

test/e2e/diagram.navigation.zoom.pan.test.ts

+38-22
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import type { Point } from '@test/shared/visu/bpmn-page-utils';
2020
import path from 'node:path';
2121

2222
import debugLogger from 'debug';
23-
import 'jest-playwright-preset';
2423

25-
import { ImageSnapshotConfigurator, MultiBrowserImageSnapshotThresholds } from './helpers/visu/image-snapshot-config';
24+
import { withCustomOutputDirectory, ImageSnapshotConfigurator, MultiBrowserImageSnapshotThresholds } from './helpers/visu/image-snapshot-config';
25+
import 'jest-playwright-preset';
2626

2727
import { ZoomType } from '@lib/component/options';
2828
import { AvailableTestPages, PageTester } from '@test/shared/visu/bpmn-page-utils';
@@ -119,11 +119,15 @@ describe('diagram navigation - zoom and pan with mouse', () => {
119119

120120
const image = await page.screenshot({ fullPage: true });
121121
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
122-
expect(image).toMatchImageSnapshot({
123-
...config,
124-
customSnapshotIdentifier: 'initial.zoom',
125-
customDiffDir: path.join(config.customDiffDir, `mouse-zoom-in-out-${xTimes}-times`),
126-
});
122+
expect(image).toMatchImageSnapshot(
123+
withCustomOutputDirectory(
124+
{
125+
...config,
126+
customSnapshotIdentifier: 'initial.zoom',
127+
},
128+
path.join(config.customDiffDir, `mouse-zoom-in-out-${xTimes}-times`),
129+
),
130+
);
127131
});
128132
});
129133

@@ -163,11 +167,15 @@ describe('diagram navigation - zoom with buttons', () => {
163167

164168
const image = await page.screenshot({ fullPage: true });
165169
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
166-
expect(image).toMatchImageSnapshot({
167-
...config,
168-
customSnapshotIdentifier: 'initial.zoom',
169-
customDiffDir: path.join(config.customDiffDir, `button-zoom-in-out-${xTimes}-times`),
170-
});
170+
expect(image).toMatchImageSnapshot(
171+
withCustomOutputDirectory(
172+
{
173+
...config,
174+
customSnapshotIdentifier: 'initial.zoom',
175+
},
176+
path.join(config.customDiffDir, `button-zoom-in-out-${xTimes}-times`),
177+
),
178+
);
171179
});
172180
});
173181

@@ -205,11 +213,15 @@ describe('diagram navigation - zoom with buttons and mouse', () => {
205213

206214
const image = await page.screenshot({ fullPage: true });
207215
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
208-
expect(image).toMatchImageSnapshot({
209-
...config,
210-
customSnapshotIdentifier: 'initial.zoom',
211-
customDiffDir: path.join(config.customDiffDir, `zoom-button-then-mouse-${firstZoom}-then-${secondZoom}`),
212-
});
216+
expect(image).toMatchImageSnapshot(
217+
withCustomOutputDirectory(
218+
{
219+
...config,
220+
customSnapshotIdentifier: 'initial.zoom',
221+
},
222+
path.join(config.customDiffDir, `zoom-button-then-mouse-${firstZoom}-then-${secondZoom}`),
223+
),
224+
);
213225
});
214226

215227
it.each`
@@ -222,10 +234,14 @@ describe('diagram navigation - zoom with buttons and mouse', () => {
222234

223235
const image = await page.screenshot({ fullPage: true });
224236
const config = imageSnapshotConfigurator.getConfig(bpmnDiagramName);
225-
expect(image).toMatchImageSnapshot({
226-
...config,
227-
customSnapshotIdentifier: 'initial.zoom',
228-
customDiffDir: path.join(config.customDiffDir, `zoom-mouse-then-button-${firstZoom}-then-${secondZoom}`),
229-
});
237+
expect(image).toMatchImageSnapshot(
238+
withCustomOutputDirectory(
239+
{
240+
...config,
241+
customSnapshotIdentifier: 'initial.zoom',
242+
},
243+
path.join(config.customDiffDir, `zoom-mouse-then-button-${firstZoom}-then-${secondZoom}`),
244+
),
245+
);
230246
});
231247
});

test/e2e/helpers/visu/image-snapshot-config.ts

+18-8
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@ export class ImageSnapshotConfigurator {
5555
const fileName = typeof parameter === 'string' ? parameter : parameter.fileName;
5656
const failureThreshold = this.getFailureThreshold(fileName);
5757

58-
return {
59-
...defaultImageSnapshotConfig,
60-
failureThreshold: failureThreshold,
61-
customSnapshotIdentifier: fileName,
62-
customSnapshotsDir: this.defaultCustomSnapshotsDir,
63-
customDiffDir: this.defaultCustomDiffDir,
64-
customReceivedDir: this.defaultCustomDiffDir,
65-
};
58+
return withCustomOutputDirectory(
59+
{
60+
...defaultImageSnapshotConfig,
61+
failureThreshold: failureThreshold,
62+
customSnapshotIdentifier: fileName,
63+
customSnapshotsDir: this.defaultCustomSnapshotsDir,
64+
},
65+
this.defaultCustomDiffDir,
66+
);
6667
}
6768

6869
private getFailureThreshold(fileName: string): number {
@@ -166,3 +167,12 @@ export class MultiBrowserImageSnapshotThresholds {
166167
}
167168
}
168169
}
170+
171+
/**
172+
* Set both the customDiffDir and customReceivedDir to the same value.
173+
*/
174+
export const withCustomOutputDirectory = (options: MatchImageSnapshotOptions, customOutputDirectory: string): MatchImageSnapshotOptions => ({
175+
...options,
176+
customDiffDir: customOutputDirectory,
177+
customReceivedDir: customOutputDirectory,
178+
});

0 commit comments

Comments
 (0)