nuxt-spec 0.2.0-alpha.8 → 0.2.0

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,89 +1,89 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
- import { resolve } from 'node:path'
3
- import { expect } from 'vitest'
4
- import { decode, type DecodedPng } from 'fast-png'
5
- import pixelmatch from 'pixelmatch'
6
- import type { NuxtPage } from '@nuxt/test-utils'
7
-
8
- export interface CompareScreenshotOptions {
9
- /** Name of the PNG file used for baseline storage and comparison (defaults to route and `index.png` for `/`) */
10
- fileName?: string
11
- /** Directory for baseline/current screenshots, relative to project root (defaults to `test/e2e`) */
12
- targetDir?: string
13
- /** CSS selector for a specific element to capture (defaults to full page) */
14
- selector?: string
15
- /** Max ratio of different pixels (0–1). Default: 0 (exact match) */
16
- maxDiffPixelRatio?: number
17
- /** Max absolute number of different pixels. Takes precedence over `maxDiffPixelRatio` when set. Default: 0 (exact match) */
18
- maxDiffPixels?: number
19
- /** Per-pixel color distance threshold (0–1). Lower = stricter. Default: 0.1 */
20
- threshold?: number
21
- }
22
-
23
- // capture a browser screenshot and compare it against a stored baseline PNG
24
- export async function compareScreenshot(page: NuxtPage, options?: CompareScreenshotOptions): Promise<boolean> {
25
- const dir = resolve(process.cwd(), options?.targetDir ?? 'test/e2e')
26
- const baselineDir = resolve(dir, '__baseline__')
27
- const currentDir = resolve(dir, '__current__')
28
-
29
- const route = page.url().substring(page.url().lastIndexOf('/') + 1) || 'index'
30
- const fileName = options?.fileName ?? `${route}.png`
31
-
32
- // capture element specified by locator or a full-page screenshot as PNG
33
- const screenshot = options?.selector
34
- ? await page.locator(options.selector).screenshot()
35
- : await page.screenshot({ fullPage: true })
36
- const baselinePath = resolve(baselineDir, fileName)
37
-
38
- // always save the current screenshot for inspection
39
- mkdirSync(currentDir, { recursive: true })
40
- writeFileSync(resolve(currentDir, fileName), screenshot)
41
-
42
- // @ts-expect-error - this is reliable way of reading Vitest "update" flag
43
- const updating = expect.getState().snapshotState?._updateSnapshot === 'all'
44
- if (updating || !existsSync(baselinePath)) {
45
- // save new baseline screenshot
46
- mkdirSync(baselineDir, { recursive: true })
47
- writeFileSync(baselinePath, screenshot)
48
- return true
49
- }
50
-
51
- // compare against stored baseline PNG using pixelmatch
52
- const baseline = readFileSync(baselinePath)
53
- const baselineImg = decode(baseline)
54
- const actualImg = decode(screenshot)
55
- const { width, height } = baselineImg
56
-
57
- if (actualImg.width !== width || actualImg.height !== height) {
58
- expect.fail(`Screenshot size mismatch: expected ${width}x${height}, got ${actualImg.width}x${actualImg.height}. Actual saved to: ${resolve(currentDir, fileName)}`)
59
- }
60
-
61
- const diffCount = pixelmatch(toRGBA(baselineImg), toRGBA(actualImg), undefined, width, height, {
62
- threshold: options?.threshold ?? 0.1,
63
- })
64
-
65
- const totalPixels = width * height
66
- const maxAllowed = options?.maxDiffPixels ?? Math.ceil(totalPixels * (options?.maxDiffPixelRatio ?? 0))
67
-
68
- if (diffCount > maxAllowed) {
69
- const ratio = (diffCount / totalPixels * 100).toFixed(2)
70
- expect.fail(`Screenshot mismatch: ${diffCount} pixels differ (${ratio}%), allowed ${maxAllowed}. Actual saved to: ${resolve(currentDir, fileName)}`)
71
- }
72
-
73
- return true
74
- }
75
-
76
- // helper for bridging difference between Vitest PNG saving and fast-png encoding
77
- function toRGBA(img: DecodedPng): Uint8Array {
78
- const { width, height, data, channels = 4 } = img
79
- if (channels === 4) return data as Uint8Array
80
- const pixels = width * height
81
- const rgba = new Uint8Array(pixels * 4)
82
- for (let i = 0; i < pixels; i++) {
83
- rgba[i * 4] = data[i * 3]
84
- rgba[i * 4 + 1] = data[i * 3 + 1]
85
- rgba[i * 4 + 2] = data[i * 3 + 2]
86
- rgba[i * 4 + 3] = 255
87
- }
88
- return rgba
89
- }
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { expect } from 'vitest'
4
+ import { decode, type DecodedPng } from 'fast-png'
5
+ import pixelmatch from 'pixelmatch'
6
+ import type { NuxtPage } from '@nuxt/test-utils'
7
+
8
+ export interface CompareScreenshotOptions {
9
+ /** Name of the PNG file used for baseline storage and comparison (defaults to route and `index.png` for `/`) */
10
+ fileName?: string
11
+ /** Directory for baseline/current screenshots, relative to project root (defaults to `test/e2e`) */
12
+ targetDir?: string
13
+ /** CSS selector for a specific element to capture (defaults to full page) */
14
+ selector?: string
15
+ /** Max ratio of different pixels (0–1). Default: 0 (exact match) */
16
+ maxDiffPixelRatio?: number
17
+ /** Max absolute number of different pixels. Takes precedence over `maxDiffPixelRatio` when set. Default: 0 (exact match) */
18
+ maxDiffPixels?: number
19
+ /** Per-pixel color distance threshold (0–1). Lower = stricter. Default: 0.1 */
20
+ threshold?: number
21
+ }
22
+
23
+ // capture a browser screenshot and compare it against a stored baseline PNG
24
+ export async function compareScreenshot(page: NuxtPage, options?: CompareScreenshotOptions): Promise<boolean> {
25
+ const dir = resolve(process.cwd(), options?.targetDir ?? 'test/e2e')
26
+ const baselineDir = resolve(dir, '__baseline__')
27
+ const currentDir = resolve(dir, '__current__')
28
+
29
+ const route = page.url().substring(page.url().lastIndexOf('/') + 1) || 'index'
30
+ const fileName = options?.fileName ?? `${route}.png`
31
+
32
+ // capture element specified by locator or a full-page screenshot as PNG
33
+ const screenshot = options?.selector
34
+ ? await page.locator(options.selector).screenshot()
35
+ : await page.screenshot({ fullPage: true })
36
+ const baselinePath = resolve(baselineDir, fileName)
37
+
38
+ // always save the current screenshot for inspection
39
+ mkdirSync(currentDir, { recursive: true })
40
+ writeFileSync(resolve(currentDir, fileName), screenshot)
41
+
42
+ // @ts-expect-error - this is reliable way of reading Vitest "update" flag
43
+ const updating = expect.getState().snapshotState?._updateSnapshot === 'all'
44
+ if (updating || !existsSync(baselinePath)) {
45
+ // save new baseline screenshot
46
+ mkdirSync(baselineDir, { recursive: true })
47
+ writeFileSync(baselinePath, screenshot)
48
+ return true
49
+ }
50
+
51
+ // compare against stored baseline PNG using pixelmatch
52
+ const baseline = readFileSync(baselinePath)
53
+ const baselineImg = decode(baseline)
54
+ const actualImg = decode(screenshot)
55
+ const { width, height } = baselineImg
56
+
57
+ if (actualImg.width !== width || actualImg.height !== height) {
58
+ expect.fail(`Screenshot size mismatch: expected ${width}x${height}, got ${actualImg.width}x${actualImg.height}. Actual saved to: ${resolve(currentDir, fileName)}`)
59
+ }
60
+
61
+ const diffCount = pixelmatch(toRGBA(baselineImg), toRGBA(actualImg), undefined, width, height, {
62
+ threshold: options?.threshold ?? 0.1,
63
+ })
64
+
65
+ const totalPixels = width * height
66
+ const maxAllowed = options?.maxDiffPixels ?? Math.ceil(totalPixels * (options?.maxDiffPixelRatio ?? 0))
67
+
68
+ if (diffCount > maxAllowed) {
69
+ const ratio = (diffCount / totalPixels * 100).toFixed(2)
70
+ expect.fail(`Screenshot mismatch: ${diffCount} pixels differ (${ratio}%), allowed ${maxAllowed}. Actual saved to: ${resolve(currentDir, fileName)}`)
71
+ }
72
+
73
+ return true
74
+ }
75
+
76
+ // helper for bridging difference between Vitest PNG saving and fast-png encoding
77
+ function toRGBA(img: DecodedPng): Uint8Array {
78
+ const { width, height, data, channels = 4 } = img
79
+ if (channels === 4) return data as Uint8Array
80
+ const pixels = width * height
81
+ const rgba = new Uint8Array(pixels * 4)
82
+ for (let i = 0; i < pixels; i++) {
83
+ rgba[i * 4] = data[i * 3]
84
+ rgba[i * 4 + 1] = data[i * 3 + 1]
85
+ rgba[i * 4 + 2] = data[i * 3 + 2]
86
+ rgba[i * 4 + 3] = 255
87
+ }
88
+ return rgba
89
+ }