vitest-plugin-vis 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../../src/server/browser_provider/playwright.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,wBAAgB,UAAU,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAYrE"}
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../../src/server/browser_provider/playwright.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,wBAAgB,UAAU,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAWrE"}
@@ -3,11 +3,10 @@ export function playwright(context) {
3
3
  async takeScreenshot(filePath, selector, options) {
4
4
  // The `Locator` type from `vitest` has less props than the `Locator` in `playwright`
5
5
  const subject = context.iframe.locator(selector ?? 'body');
6
- const buffer = await subject.screenshot({
6
+ return subject.screenshot({
7
7
  timeout: options?.timeout,
8
8
  path: filePath,
9
9
  });
10
- return buffer.toString('base64');
11
10
  },
12
11
  };
13
12
  }
@@ -1,6 +1,6 @@
1
1
  export interface BrowserApi {
2
2
  takeScreenshot(filePath: string, selector: string, options: {
3
3
  timeout?: number | undefined;
4
- } | undefined): Promise<string>;
4
+ } | undefined): Promise<Buffer>;
5
5
  }
6
6
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webdriverio.d.ts","sourceRoot":"","sources":["../../../src/server/browser_provider/webdriverio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,wBAAgB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAStE"}
1
+ {"version":3,"file":"webdriverio.d.ts","sourceRoot":"","sources":["../../../src/server/browser_provider/webdriverio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,wBAAgB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAQtE"}
@@ -3,8 +3,7 @@ export function webdriverio(context) {
3
3
  return {
4
4
  async takeScreenshot(filePath, selector) {
5
5
  const element = await page.$(`${selector ?? 'body'}`);
6
- const buffer = await element.saveScreenshot(filePath);
7
- return buffer.toString('base64');
6
+ return element.saveScreenshot(filePath);
8
7
  },
9
8
  };
10
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/match_image_snapshot.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,aAAa,CAAA;AAIxE,OAAO,KAAK,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,uBAAuB,CAAA;AAM9B,MAAM,WAAW,yBAAyB;IACzC,kBAAkB,EAAE,CACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS,KAC3C,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED,MAAM,WAAW,yBAChB,SAAQ,2BAA2B,EAClC,sBAAsB,EACtB,2BAA2B;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC9C;IAAC,MAAM,EAAE,MAAM;IAAE,OAAO,EAAE,MAAM;IAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS;CAAC,CA2DlF,CAAA"}
1
+ {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/match_image_snapshot.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,aAAa,CAAA;AAIxE,OAAO,KAAK,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,uBAAuB,CAAA;AAM9B,MAAM,WAAW,yBAAyB;IACzC,kBAAkB,EAAE,CACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS,KAC3C,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED,MAAM,WAAW,yBAChB,SAAQ,2BAA2B,EAClC,sBAAsB,EACtB,2BAA2B;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC9C;IAAC,MAAM,EAAE,MAAM;IAAE,OAAO,EAAE,MAAM;IAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS;CAAC,CA6DlF,CAAA"}
@@ -19,20 +19,23 @@ export const matchImageSnapshot = async (context, taskId, subject, options) => {
19
19
  options = {};
20
20
  options.timeout = options.timeout ?? (ci ? 30000 : 5000);
21
21
  const info = visContext.getSnapshotInfo(context.testPath, taskId, options);
22
- const baselineBase64 = await file.tryReadFileBase64(info.baselinePath);
23
- if (!baselineBase64) {
24
- await takeSnapshot(context, subject, info.baselinePath, options);
22
+ const baselineBuffer = await file.tryReadFile(info.baselinePath);
23
+ if (!baselineBuffer) {
24
+ if (isBase64String(subject)) {
25
+ await writeSnapshot(subject, info.baselinePath);
26
+ }
27
+ else {
28
+ await takeSnapshotByBrowser(context, subject, info.baselinePath, options);
29
+ }
25
30
  return;
26
31
  }
27
- const resultBase64 = await takeSnapshot(context, subject, info.resultPath, options);
28
- const baselineImage = PNG.sync.read(Buffer.from(baselineBase64, 'base64'));
29
- const resultImage = PNG.sync.read(Buffer.from(resultBase64, 'base64'));
30
- const [baselineAlignedImage, resultAlignedImage] = isSameSize(baselineImage, resultImage)
31
- ? [baselineImage, resultImage]
32
- : alignImageSizes(baselineImage, resultImage);
32
+ const resultBuffer = await takeSnapshot(context, subject, info.resultPath, options);
33
+ const baselineImage = PNG.sync.read(baselineBuffer, { skipRescale: true, checkCRC: false });
34
+ const resultImage = PNG.sync.read(resultBuffer, { skipRescale: true, checkCRC: false });
35
+ const [baselineAlignedImage, resultAlignedImage] = alignImageSizes(baselineImage, resultImage);
33
36
  const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options);
34
37
  if (pass) {
35
- if (sizeNotChanged(baselineImage, baselineAlignedImage)) {
38
+ if (sizeNotChanged(baselineImage, baselineAlignedImage) && sizeNotChanged(resultImage, resultAlignedImage)) {
36
39
  return;
37
40
  }
38
41
  throw new Error(dedent `Snapshot \`${taskId}\` mismatched
@@ -60,8 +63,11 @@ export const matchImageSnapshot = async (context, taskId, subject, options) => {
60
63
  };
61
64
  async function takeSnapshot(context, subject, filePath, options) {
62
65
  if (isBase64String(subject)) {
63
- return writeSnapshot(subject, filePath);
66
+ return Buffer.from(await writeSnapshot(subject, filePath), 'base64');
64
67
  }
68
+ return takeSnapshotByBrowser(context, subject, filePath, options);
69
+ }
70
+ async function takeSnapshotByBrowser(context, subject, filePath, options) {
65
71
  await mkdirp(dirname(filePath));
66
72
  const browser = browserApi(context);
67
73
  return browser.takeScreenshot(filePath, subject, {
@@ -74,14 +80,17 @@ async function writeSnapshot(subject, filePath) {
74
80
  return subject;
75
81
  }
76
82
  function alignImageSizes(baseline, result) {
83
+ if (isSameSize(baseline, result))
84
+ return [baseline, result];
77
85
  const size = getMaxSize(baseline, result);
78
- const baselineAligned = new PNG(size);
79
- const resultAligned = new PNG(size);
80
- baselineAligned.data.fill(0);
81
- resultAligned.data.fill(0);
82
- PNG.bitblt(baseline, baselineAligned, 0, 0, baseline.width, baseline.height);
83
- PNG.bitblt(result, resultAligned, 0, 0, result.width, result.height);
84
- return [baselineAligned, resultAligned];
86
+ return [resizeImage(baseline, size), resizeImage(result, size)];
87
+ }
88
+ function resizeImage(image, size) {
89
+ if (isSameSize(image, size))
90
+ return image;
91
+ const resized = new PNG(size);
92
+ PNG.bitblt(image, resized, 0, 0, image.width, image.height);
93
+ return resized;
85
94
  }
86
95
  function sizeNotChanged(baselineImage, baselineAlignedImage) {
87
96
  return baselineImage === baselineAlignedImage;
@@ -1,5 +1,5 @@
1
1
  export declare const file: {
2
- tryReadFileBase64(filePath: string): Promise<string | undefined>;
2
+ tryReadFile(filePath: string): Promise<Buffer | undefined>;
3
3
  writeFileBase64(filePath: string, data: string): Promise<void>;
4
4
  existFile(filePath: string): Promise<boolean>;
5
5
  };
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/server/file.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI;gCACkB,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;8BAG5C,MAAM,QAAQ,MAAM;wBAG1B,MAAM;CAK1B,CAAA"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/server/file.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI;0BACY,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;8BAGtC,MAAM,QAAQ,MAAM;wBAG1B,MAAM;CAK1B,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { readFile, stat, writeFile } from 'node:fs/promises';
2
2
  export const file = {
3
- async tryReadFileBase64(filePath) {
4
- return readFile(filePath, { encoding: 'base64' }).catch(() => undefined);
3
+ async tryReadFile(filePath) {
4
+ return readFile(filePath).catch(() => undefined);
5
5
  },
6
6
  writeFileBase64(filePath, data) {
7
7
  return writeFile(filePath, data, { encoding: 'base64' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-plugin-vis",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Vitest visual testing plugin",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -6,11 +6,10 @@ export function playwright(context: BrowserCommandContext): BrowserApi {
6
6
  async takeScreenshot(filePath, selector, options) {
7
7
  // The `Locator` type from `vitest` has less props than the `Locator` in `playwright`
8
8
  const subject = context.iframe.locator(selector ?? 'body')
9
- const buffer = await subject.screenshot({
9
+ return subject.screenshot({
10
10
  timeout: options?.timeout,
11
11
  path: filePath,
12
12
  })
13
- return buffer.toString('base64')
14
13
  },
15
14
  }
16
15
  }
@@ -7,5 +7,5 @@ export interface BrowserApi {
7
7
  timeout?: number | undefined
8
8
  }
9
9
  | undefined,
10
- ): Promise<string>
10
+ ): Promise<Buffer>
11
11
  }
@@ -6,8 +6,7 @@ export function webdriverio(context: BrowserCommandContext): BrowserApi {
6
6
  return {
7
7
  async takeScreenshot(filePath, selector) {
8
8
  const element = await page.$(`${selector ?? 'body'}`)
9
- const buffer = await element.saveScreenshot(filePath)
10
- return buffer.toString('base64')
9
+ return element.saveScreenshot(filePath)
11
10
  },
12
11
  }
13
12
  }
@@ -47,22 +47,24 @@ export const matchImageSnapshot: BrowserCommand<
47
47
  options.timeout = options.timeout ?? (ci ? 30000 : 5000)
48
48
 
49
49
  const info = visContext.getSnapshotInfo(context.testPath, taskId, options)
50
- const baselineBase64 = await file.tryReadFileBase64(info.baselinePath)
51
- if (!baselineBase64) {
52
- await takeSnapshot(context, subject, info.baselinePath, options)
50
+ const baselineBuffer = await file.tryReadFile(info.baselinePath)
51
+ if (!baselineBuffer) {
52
+ if (isBase64String(subject)) {
53
+ await writeSnapshot(subject, info.baselinePath)
54
+ } else {
55
+ await takeSnapshotByBrowser(context, subject, info.baselinePath, options)
56
+ }
53
57
  return
54
58
  }
55
59
 
56
- const resultBase64 = await takeSnapshot(context, subject, info.resultPath, options)
57
- const baselineImage = PNG.sync.read(Buffer.from(baselineBase64, 'base64'))
58
- const resultImage = PNG.sync.read(Buffer.from(resultBase64, 'base64'))
59
- const [baselineAlignedImage, resultAlignedImage] = isSameSize(baselineImage, resultImage)
60
- ? [baselineImage, resultImage]
61
- : alignImageSizes(baselineImage, resultImage)
60
+ const resultBuffer = await takeSnapshot(context, subject, info.resultPath, options)
61
+ const baselineImage = PNG.sync.read(baselineBuffer, { skipRescale: true, checkCRC: false })
62
+ const resultImage = PNG.sync.read(resultBuffer, { skipRescale: true, checkCRC: false })
63
+ const [baselineAlignedImage, resultAlignedImage] = alignImageSizes(baselineImage, resultImage)
62
64
 
63
65
  const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options)
64
66
  if (pass) {
65
- if (sizeNotChanged(baselineImage, baselineAlignedImage)) {
67
+ if (sizeNotChanged(baselineImage, baselineAlignedImage) && sizeNotChanged(resultImage, resultAlignedImage)) {
66
68
  return
67
69
  }
68
70
  throw new Error(
@@ -104,9 +106,17 @@ async function takeSnapshot(
104
106
  options: ImageSnapshotTimeoutOptions | undefined,
105
107
  ) {
106
108
  if (isBase64String(subject)) {
107
- return writeSnapshot(subject, filePath)
109
+ return Buffer.from(await writeSnapshot(subject, filePath), 'base64')
108
110
  }
111
+ return takeSnapshotByBrowser(context, subject, filePath, options)
112
+ }
109
113
 
114
+ async function takeSnapshotByBrowser(
115
+ context: BrowserCommandContext,
116
+ subject: string,
117
+ filePath: string,
118
+ options: ImageSnapshotTimeoutOptions | undefined,
119
+ ) {
110
120
  await mkdirp(dirname(filePath))
111
121
  const browser = browserApi(context)
112
122
  return browser.takeScreenshot(filePath, subject, {
@@ -121,19 +131,20 @@ async function writeSnapshot(subject: string, filePath: string) {
121
131
  }
122
132
 
123
133
  function alignImageSizes(baseline: PNG, result: PNG) {
124
- const size = getMaxSize(baseline, result)
125
-
126
- const baselineAligned = new PNG(size)
127
- const resultAligned = new PNG(size)
134
+ if (isSameSize(baseline, result)) return [baseline, result] as const
128
135
 
129
- baselineAligned.data.fill(0)
130
- resultAligned.data.fill(0)
136
+ const size = getMaxSize(baseline, result)
137
+ return [resizeImage(baseline, size), resizeImage(result, size)] as const
138
+ }
131
139
 
132
- PNG.bitblt(baseline, baselineAligned, 0, 0, baseline.width, baseline.height)
133
- PNG.bitblt(result, resultAligned, 0, 0, result.width, result.height)
140
+ function resizeImage(image: PNG, size: { width: number; height: number }) {
141
+ if (isSameSize(image, size)) return image
134
142
 
135
- return [baselineAligned, resultAligned] as const
143
+ const resized = new PNG(size)
144
+ PNG.bitblt(image, resized, 0, 0, image.width, image.height)
145
+ return resized
136
146
  }
147
+
137
148
  function sizeNotChanged(baselineImage: PNG, baselineAlignedImage: PNG) {
138
149
  return baselineImage === baselineAlignedImage
139
150
  }
@@ -1,8 +1,8 @@
1
1
  import { readFile, stat, writeFile } from 'node:fs/promises'
2
2
 
3
3
  export const file = {
4
- async tryReadFileBase64(filePath: string): Promise<string | undefined> {
5
- return readFile(filePath, { encoding: 'base64' }).catch(() => undefined)
4
+ async tryReadFile(filePath: string): Promise<Buffer | undefined> {
5
+ return readFile(filePath).catch(() => undefined)
6
6
  },
7
7
  writeFileBase64(filePath: string, data: string) {
8
8
  return writeFile(filePath, data, { encoding: 'base64' })