Skip to content

Commit c3b44a4

Browse files
committed
fix: autocleanup removes screenshots of other testing type
add test case to cover this scenario add isImageOfTestType check resolves #178 Signed-off-by: Jakub Freisler <[email protected]>
1 parent 5f59a2b commit c3b44a4

File tree

5 files changed

+92
-41
lines changed

5 files changed

+92
-41
lines changed
68 Bytes
Loading

__tests__/fixtures/screenshot.png

68 Bytes
Loading

src/image.utils.ts

+33-11
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,41 @@ import { version } from "../package.json";
88
import { wasScreenshotUsed } from "./screenshotPath.utils";
99
import { METADATA_KEY } from "./constants";
1010

11-
export const addPNGMetadata = (png: Buffer) =>
12-
addMetadata(png, METADATA_KEY, version /* c8 ignore next */);
13-
export const getPNGMetadata = (png: Buffer) =>
14-
getMetadata(png, METADATA_KEY /* c8 ignore next */);
11+
type PluginMetadata = {
12+
version: string;
13+
testingType?: 'e2e' | 'component';
14+
};
15+
16+
type PluginMetadataConfig = {
17+
testingType?: string;
18+
};
19+
20+
export const addPNGMetadata = (config: PluginMetadataConfig, png: Buffer) =>
21+
addMetadata(png, METADATA_KEY, JSON.stringify({ version, testingType: config.testingType || 'e2e' } as PluginMetadata) /* c8 ignore next */);
22+
export const getPNGMetadata = (png: Buffer): PluginMetadata | undefined => {
23+
const metadataString = getMetadata(png, METADATA_KEY /* c8 ignore next */);
24+
25+
if (metadataString === undefined) return;
26+
try {
27+
return JSON.parse(metadataString);
28+
} catch {
29+
return { version: metadataString };
30+
}
31+
}
1532
export const isImageCurrentVersion = (png: Buffer) =>
16-
getPNGMetadata(png) === version;
33+
getPNGMetadata(png)?.version === version;
1734
export const isImageGeneratedByPlugin = (png: Buffer) =>
1835
!!getPNGMetadata(png /* c8 ignore next */);
36+
export const isImageOfTestType = (png: Buffer, testingType?: PluginMetadataConfig['testingType']) => {
37+
if (!isImageGeneratedByPlugin(png)) return false;
38+
const imageTestingType = getPNGMetadata(png /* c8 ignore next */)?.testingType;
39+
return imageTestingType === testingType || testingType === imageTestingType === undefined;
40+
};
1941

20-
export const writePNG = (name: string, png: PNG | Buffer) =>
42+
export const writePNG = (config: PluginMetadataConfig, name: string, png: PNG | Buffer) =>
2143
fs.writeFileSync(
2244
name,
23-
addPNGMetadata(png instanceof PNG ? PNG.sync.write(png) : png)
45+
addPNGMetadata(config, png instanceof PNG ? PNG.sync.write(png) : png)
2446
);
2547

2648
const inArea = (x: number, y: number, height: number, width: number) =>
@@ -95,17 +117,17 @@ export const alignImagesToSameSize = (
95117
];
96118
};
97119

98-
export const cleanupUnused = (rootPath: string) => {
120+
export const cleanupUnused = (config: PluginMetadataConfig & { projectRoot: string; }) => {
99121
glob
100122
.sync("**/*.png", {
101-
cwd: rootPath,
123+
cwd: config.projectRoot,
102124
ignore: "node_modules/**/*",
103125
})
104126
.forEach((pngPath) => {
105-
const absolutePath = path.join(rootPath, pngPath);
127+
const absolutePath = path.join(config.projectRoot, pngPath);
106128
if (
107129
!wasScreenshotUsed(pngPath) &&
108-
isImageGeneratedByPlugin(fs.readFileSync(absolutePath))
130+
isImageOfTestType(fs.readFileSync(absolutePath), config.testingType)
109131
) {
110132
fs.unlinkSync(absolutePath);
111133
}

src/task.hook.test.ts

+53-26
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe("getScreenshotPathInfoTask", () => {
4848
titleFromOptions: "some-title-withśpęćiał人物",
4949
imagesPath: "nested/images/dir",
5050
specPath,
51+
currentRetryNumber: 0,
5152
})
5253
).toEqual({
5354
screenshotPath:
@@ -62,6 +63,7 @@ describe("getScreenshotPathInfoTask", () => {
6263
titleFromOptions: "some-title",
6364
imagesPath: "{spec_path}/images/dir",
6465
specPath,
66+
currentRetryNumber: 0,
6567
})
6668
).toEqual({
6769
screenshotPath:
@@ -76,6 +78,7 @@ describe("getScreenshotPathInfoTask", () => {
7678
titleFromOptions: "some-title",
7779
imagesPath: "/images/dir",
7880
specPath,
81+
currentRetryNumber: 0,
7982
})
8083
).toEqual({
8184
screenshotPath:
@@ -88,6 +91,7 @@ describe("getScreenshotPathInfoTask", () => {
8891
titleFromOptions: "some-title",
8992
imagesPath: "C:/images/dir",
9093
specPath,
94+
currentRetryNumber: 0,
9195
})
9296
).toEqual({
9397
screenshotPath:
@@ -104,6 +108,7 @@ describe("cleanupImagesTask", () => {
104108
titleFromOptions: "some-file",
105109
imagesPath: "images",
106110
specPath: "some/spec/path",
111+
currentRetryNumber: 0,
107112
});
108113
return path.join(
109114
projectRoot,
@@ -113,34 +118,56 @@ describe("cleanupImagesTask", () => {
113118
);
114119
};
115120

116-
it("does not remove used screenshot", async () => {
117-
const { path: projectRoot } = await dir();
118-
const screenshotPath = await writeTmpFixture(
119-
await generateUsedScreenshotPath(projectRoot),
120-
oldImgFixture
121-
);
121+
describe('when testing type does not match', () => {
122+
it("does not remove unused screenshot", async () => {
123+
const { path: projectRoot } = await dir();
124+
const screenshotPath = await writeTmpFixture(
125+
path.join(projectRoot, "some-file-2 #0.png"),
126+
oldImgFixture
127+
);
122128

123-
cleanupImagesTask({
124-
projectRoot,
125-
env: { pluginVisualRegressionCleanupUnusedImages: true },
126-
} as unknown as Cypress.PluginConfigOptions);
129+
cleanupImagesTask({
130+
projectRoot,
131+
env: { pluginVisualRegressionCleanupUnusedImages: true },
132+
testingType: 'component',
133+
} as unknown as Cypress.PluginConfigOptions);
127134

128-
expect(existsSync(screenshotPath)).toBe(true);
135+
expect(existsSync(screenshotPath)).toBe(true);
136+
});
129137
});
130138

131-
it("removes unused screenshot", async () => {
132-
const { path: projectRoot } = await dir();
133-
const screenshotPath = await writeTmpFixture(
134-
path.join(projectRoot, "some-file-2 #0.png"),
135-
oldImgFixture
136-
);
139+
describe('when testing type matches', () => {
140+
it("does not remove used screenshot", async () => {
141+
const { path: projectRoot } = await dir();
142+
const screenshotPath = await writeTmpFixture(
143+
await generateUsedScreenshotPath(projectRoot),
144+
oldImgFixture
145+
);
137146

138-
cleanupImagesTask({
139-
projectRoot,
140-
env: { pluginVisualRegressionCleanupUnusedImages: true },
141-
} as unknown as Cypress.PluginConfigOptions);
147+
cleanupImagesTask({
148+
projectRoot,
149+
env: { pluginVisualRegressionCleanupUnusedImages: true },
150+
testingType: 'e2e',
151+
} as unknown as Cypress.PluginConfigOptions);
142152

143-
expect(existsSync(screenshotPath)).toBe(false);
153+
expect(existsSync(screenshotPath)).toBe(true);
154+
});
155+
156+
it("removes unused screenshot", async () => {
157+
const { path: projectRoot } = await dir();
158+
const screenshotPath = await writeTmpFixture(
159+
path.join(projectRoot, "some-file-2 #0.png"),
160+
oldImgFixture
161+
);
162+
163+
cleanupImagesTask({
164+
projectRoot,
165+
env: { pluginVisualRegressionCleanupUnusedImages: true },
166+
testingType: 'e2e',
167+
} as unknown as Cypress.PluginConfigOptions);
168+
169+
expect(existsSync(screenshotPath)).toBe(false);
170+
});
144171
});
145172
});
146173
});
@@ -178,7 +205,7 @@ describe("compareImagesTask", () => {
178205
describe("when old screenshot exists", () => {
179206
it("resolves with a success message", async () =>
180207
expect(
181-
compareImagesTask(await generateConfig({ updateImages: true }))
208+
compareImagesTask({ testingType: 'e2e' }, await generateConfig({ updateImages: true }))
182209
).resolves.toEqual({
183210
message:
184211
"Image diff factor (0%) is within boundaries of maximum threshold option 0.5.",
@@ -197,7 +224,7 @@ describe("compareImagesTask", () => {
197224
const cfg = await generateConfig({ updateImages: false });
198225
await fs.unlink(cfg.imgOld);
199226

200-
await expect(compareImagesTask(cfg)).resolves.toEqual({
227+
await expect(compareImagesTask({ testingType: 'e2e' }, cfg)).resolves.toEqual({
201228
message:
202229
"Image diff factor (0%) is within boundaries of maximum threshold option 0.5.",
203230
imgDiff: 0,
@@ -214,7 +241,7 @@ describe("compareImagesTask", () => {
214241
it("resolves with an error message", async () => {
215242
const cfg = await generateConfig({ updateImages: false });
216243

217-
await expect(compareImagesTask(cfg)).resolves.toMatchSnapshot();
244+
await expect(compareImagesTask({ testingType: 'e2e' }, cfg)).resolves.toMatchSnapshot();
218245
});
219246
});
220247

@@ -223,7 +250,7 @@ describe("compareImagesTask", () => {
223250
const cfg = await generateConfig({ updateImages: false });
224251
await writeTmpFixture(cfg.imgNew, oldImgFixture);
225252

226-
await expect(compareImagesTask(cfg)).resolves.toMatchSnapshot();
253+
await expect(compareImagesTask({ testingType: 'e2e' }, cfg)).resolves.toMatchSnapshot();
227254
});
228255
});
229256
});

src/task.hook.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const getScreenshotPathInfoTask = (cfg: {
4747

4848
export const cleanupImagesTask = (config: Cypress.PluginConfigOptions) => {
4949
if (config.env["pluginVisualRegressionCleanupUnusedImages"]) {
50-
cleanupUnused(config.projectRoot);
50+
cleanupUnused(config);
5151
}
5252

5353
resetScreenshotNameCache();
@@ -68,6 +68,7 @@ export const approveImageTask = ({ img }: { img: string }) => {
6868
};
6969

7070
export const compareImagesTask = async (
71+
cypressConfig: { testingType: string },
7172
cfg: CompareImagesCfg
7273
): Promise<CompareImagesTaskReturn> => {
7374
const messages = [] as string[];
@@ -127,6 +128,7 @@ export const compareImagesTask = async (
127128

128129
if (error) {
129130
writePNG(
131+
cypressConfig,
130132
cfg.imgNew.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff),
131133
diffBuffer
132134
);
@@ -141,7 +143,7 @@ export const compareImagesTask = async (
141143
};
142144
} else {
143145
if (rawImgOld && !isImageCurrentVersion(rawImgOldBuffer)) {
144-
writePNG(cfg.imgNew, rawImgNewBuffer);
146+
writePNG(cypressConfig, cfg.imgNew, rawImgNewBuffer);
145147
moveFile.sync(cfg.imgNew, cfg.imgOld);
146148
} else {
147149
// don't overwrite file if it's the same (imgDiff < cfg.maxDiffThreshold && !isImgSizeDifferent)
@@ -154,7 +156,7 @@ export const compareImagesTask = async (
154156
imgNewBase64 = "";
155157
imgDiffBase64 = "";
156158
imgOldBase64 = "";
157-
writePNG(cfg.imgNew, rawImgNewBuffer);
159+
writePNG(cypressConfig, cfg.imgNew, rawImgNewBuffer);
158160
moveFile.sync(cfg.imgNew, cfg.imgOld);
159161
}
160162

@@ -189,6 +191,6 @@ export const initTaskHook = (config: Cypress.PluginConfigOptions) => ({
189191
[TASK.cleanupImages]: cleanupImagesTask.bind(undefined, config),
190192
[TASK.doesFileExist]: doesFileExistTask,
191193
[TASK.approveImage]: approveImageTask,
192-
[TASK.compareImages]: compareImagesTask,
194+
[TASK.compareImages]: compareImagesTask.bind(undefined, config),
193195
});
194196
/* c8 ignore stop */

0 commit comments

Comments
 (0)