Skip to content

Commit 8d69632

Browse files
committed
feat: add forceDeviceFactor option
when forceDeviceFactor=true scale images to match 1x device scale factor set additional chrome flags to make sure of proper device scale factor Signed-off-by: Jakub Freisler <[email protected]>
1 parent 8bb9bb0 commit 8d69632

File tree

5 files changed

+388
-18
lines changed

5 files changed

+388
-18
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ cy.matchImage({
134134
imagesDir: 'this-might-be-your-custom/maybe-nested-directory',
135135
// maximum threshold above which the test should fail
136136
// default: 0.01
137-
maxDiffThreshold: 0.1
137+
maxDiffThreshold: 0.1,
138+
// forces scale factor to be set as value "1"
139+
// helps with screenshots being scaled 2x on high-density screens like Mac Retina
140+
// default: true
141+
forceDeviceScaleFactor: false,
138142
})
139143
```
140144

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@semantic-release/release-notes-generator": "^10.0.2",
3434
"@types/pixelmatch": "^5.2.4",
3535
"@types/pngjs": "^6.0.1",
36+
"@types/sharp": "^0.29.5",
3637
"@typescript-eslint/eslint-plugin": "^5.1.0",
3738
"@typescript-eslint/parser": "^5.1.0",
3839
"cypress": "^8.6.0",
@@ -63,7 +64,8 @@
6364
"dependencies": {
6465
"move-file": "^2.1.0",
6566
"pixelmatch": "^5.2.1",
66-
"pngjs": "^6.0.0"
67+
"pngjs": "^6.0.0",
68+
"sharp": "^0.30.1"
6769
},
6870
"publishConfig": {
6971
"access": "public"

src/commands.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ Cypress.Commands.add(
3737
nameCacheCounter[title] = -1;
3838
title += ` #${++nameCacheCounter[title]}`;
3939

40+
const scaleFactor =
41+
Cypress.env("pluginVisualRegressionForceDeviceScaleFactor") === false
42+
? 1
43+
: 1 / window.devicePixelRatio;
44+
4045
const updateImages =
4146
options.updateImages ||
4247
(Cypress.env("pluginVisualRegressionUpdateImages") as
@@ -97,6 +102,7 @@ Cypress.Commands.add(
97102
.task(
98103
TASK.compareImages,
99104
{
105+
scaleFactor,
100106
imgNew: imgPath,
101107
imgOld: imgPath.replace(FILE_SUFFIX.actual, ""),
102108
updateImages,

src/plugins.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import path from "path";
22
import pixelmatch from "pixelmatch";
33
import fs from "fs";
4-
import { PNG } from "pngjs";
4+
import { PNG, PNGWithMetadata } from "pngjs";
55
import { FILE_SUFFIX, IMAGE_SNAPSHOT_PREFIX, TASK } from "./constants";
66
import moveFile from "move-file";
7+
import sharp from "sharp";
78

89
type NotFalsy<T> = T extends false | null | undefined ? never : T;
910

@@ -29,7 +30,25 @@ const fillSizeDifference = (width: number, height: number) => (image: PNG) => {
2930
return image;
3031
};
3132

32-
const alignImagesToSameSize = (firstImage: PNG, secondImage: PNG) => {
33+
const importAndScaleImage = async (cfg: {
34+
scaleFactor: number;
35+
path: string;
36+
}) => {
37+
const imgBuffer = fs.readFileSync(cfg.path);
38+
const rawImgNew = PNG.sync.read(imgBuffer);
39+
if (cfg.scaleFactor === 1) return rawImgNew;
40+
41+
const newImageWidth = Math.ceil(rawImgNew.width * cfg.scaleFactor);
42+
const newImageHeight = Math.ceil(rawImgNew.height * cfg.scaleFactor);
43+
await sharp(imgBuffer).resize(newImageWidth, newImageHeight).toFile(cfg.path);
44+
45+
return PNG.sync.read(fs.readFileSync(cfg.path));
46+
};
47+
48+
const alignImagesToSameSize = (
49+
firstImage: PNGWithMetadata,
50+
secondImage: PNGWithMetadata
51+
) => {
3352
const firstImageWidth = firstImage.width;
3453
const firstImageHeight = firstImage.height;
3554
const secondImageWidth = secondImage.width;
@@ -63,6 +82,21 @@ export const initPlugin = (
6382
on: Cypress.PluginEvents,
6483
config: Cypress.PluginConfigOptions
6584
) => {
85+
if (config.env["pluginVisualRegressionForceDeviceScaleFactor"] !== false) {
86+
// based on https://github.com/cypress-io/cypress/issues/2102#issuecomment-521299946
87+
on("before:browser:launch", (browser, launchOptions) => {
88+
if (browser.name === "chrome" || browser.name === "chromium") {
89+
launchOptions.args.push("--force-device-scale-factor=1");
90+
launchOptions.args.push("--high-dpi-support=1");
91+
} else if (browser.name === "electron" && browser.isHeaded) {
92+
// eslint-disable-next-line no-console
93+
console.log(
94+
"There isn't currently a way of setting the device scale factor in Cypress when running headed electron so we disable the image regression commands."
95+
);
96+
}
97+
});
98+
}
99+
66100
on("task", {
67101
[TASK.getScreenshotPath]({ title, imagesDir, specPath }) {
68102
return path.join(
@@ -86,8 +120,9 @@ export const initPlugin = (
86120

87121
return null;
88122
},
89-
[TASK.compareImages](
123+
async [TASK.compareImages](
90124
cfg: {
125+
scaleFactor: number;
91126
title: string;
92127
imgNew: string;
93128
imgOld: string;
@@ -100,7 +135,10 @@ export const initPlugin = (
100135
let errorMsg: string | undefined;
101136

102137
if (fs.existsSync(cfg.imgOld) && !cfg.updateImages) {
103-
const rawImgNew = PNG.sync.read(fs.readFileSync(cfg.imgNew));
138+
const rawImgNew = await importAndScaleImage({
139+
scaleFactor: cfg.scaleFactor,
140+
path: cfg.imgNew,
141+
});
104142
const rawImgOld = PNG.sync.read(fs.readFileSync(cfg.imgOld));
105143
const isImgSizeDifferent =
106144
rawImgNew.height !== rawImgOld.height ||

0 commit comments

Comments
 (0)