@vizzly-testing/honeydiff 0.10.1 โ 0.10.2
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.
- package/CHANGELOG.md +13 -0
- package/README.md +33 -31
- package/examples/basic.js +43 -278
- package/examples/buffers.js +1 -1
- package/examples/typescript.ts +1 -1
- package/examples/wcag.js +1 -3
- package/index.d.ts +2 -1
- package/package.json +2 -2
- package/platforms/index-aarch64-apple-darwin.node +0 -0
- package/platforms/index-aarch64-unknown-linux-gnu.node +0 -0
- package/platforms/index-x86_64-pc-windows-msvc.node +0 -0
- package/platforms/index-x86_64-unknown-linux-gnu.node +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.10.2] - 2026-05-19
|
|
9
|
+
|
|
10
|
+
## What's Changed
|
|
11
|
+
|
|
12
|
+
- ๐ ๏ธ Remove release npm self-upgrade (#30)
|
|
13
|
+
- ๐ ๏ธ Harden release workflows (#29)
|
|
14
|
+
- ๐งช Give coverage tests more time (#28)
|
|
15
|
+
- ๐ก๏ธ Harden honeydiff production contracts (#27)
|
|
16
|
+
- ๐ Fix honeydiff-core clippy warnings
|
|
17
|
+
- ๐งช Add diff contract test suite
|
|
18
|
+
|
|
19
|
+
**Full Changelog**: https://github.com/vizzly-testing/honeydiff/compare/v0.10.1...v0.10.2
|
|
20
|
+
|
|
8
21
|
## [0.10.1] - 2026-03-11
|
|
9
22
|
|
|
10
23
|
## What's Changed
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Built for production visual regression testing at scale.
|
|
|
28
28
|
### Basic Comparison
|
|
29
29
|
|
|
30
30
|
```javascript
|
|
31
|
-
|
|
31
|
+
import { compare, quickCompare } from '@vizzly-testing/honeydiff';
|
|
32
32
|
|
|
33
33
|
// Quick boolean check - are these images different?
|
|
34
34
|
if (await quickCompare('baseline.png', 'current.png')) {
|
|
@@ -37,7 +37,7 @@ if (await quickCompare('baseline.png', 'current.png')) {
|
|
|
37
37
|
|
|
38
38
|
// Detailed comparison with tolerance
|
|
39
39
|
const result = await compare('baseline.png', 'current.png', {
|
|
40
|
-
|
|
40
|
+
threshold: 10, // Ignore tiny differences
|
|
41
41
|
includeSSIM: true // Perceptual similarity score
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -52,7 +52,7 @@ console.log(`SSIM: ${result.perceptualScore}`);
|
|
|
52
52
|
import { compare, CompareOptions, DiffResult } from '@vizzly-testing/honeydiff';
|
|
53
53
|
|
|
54
54
|
const options: CompareOptions = {
|
|
55
|
-
|
|
55
|
+
threshold: 5,
|
|
56
56
|
includeSSIM: true,
|
|
57
57
|
includeClusters: true
|
|
58
58
|
};
|
|
@@ -79,14 +79,14 @@ test('homepage should match baseline', async () => {
|
|
|
79
79
|
|
|
80
80
|
**Playwright**
|
|
81
81
|
```javascript
|
|
82
|
-
|
|
82
|
+
import { compare } from '@vizzly-testing/honeydiff';
|
|
83
83
|
|
|
84
84
|
test('button hover state', async () => {
|
|
85
85
|
await page.hover('[data-testid="submit-button"]');
|
|
86
86
|
let screenshot = await page.screenshot();
|
|
87
87
|
|
|
88
88
|
let result = await compare('baseline.png', screenshot, {
|
|
89
|
-
|
|
89
|
+
threshold: 10,
|
|
90
90
|
diffPath: './artifacts/diff.png'
|
|
91
91
|
});
|
|
92
92
|
|
|
@@ -256,26 +256,22 @@ GMSD is ideal for catching:
|
|
|
256
256
|
|
|
257
257
|
**Reference:** Xue et al. 2014 - "Gradient Magnitude Similarity Deviation: A Highly Efficient Perceptual Image Quality Index"
|
|
258
258
|
|
|
259
|
-
### 7.
|
|
259
|
+
### 7. Thresholds
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
```javascript
|
|
263
|
-
const result = await compare('img1.png', 'img2.png', {
|
|
264
|
-
pixelTolerance: 10 // Ignore differences below threshold (0-255)
|
|
265
|
-
});
|
|
266
|
-
```
|
|
261
|
+
Honeydiff uses CIEDE2000 Delta E thresholds for perceptual color difference:
|
|
267
262
|
|
|
268
|
-
**YIQ Mode** - Perceptual color matching weighted for human vision:
|
|
269
263
|
```javascript
|
|
270
264
|
const result = await compare('img1.png', 'img2.png', {
|
|
271
|
-
|
|
265
|
+
threshold: 2.0 // Default: ignores tiny perceptual variance
|
|
272
266
|
});
|
|
273
267
|
```
|
|
274
268
|
|
|
275
|
-
|
|
269
|
+
Use exact mode when every changed pixel should count:
|
|
270
|
+
|
|
276
271
|
```javascript
|
|
277
272
|
const result = await compare('img1.png', 'img2.png', {
|
|
278
|
-
|
|
273
|
+
threshold: 0,
|
|
274
|
+
minClusterSize: 1
|
|
279
275
|
});
|
|
280
276
|
```
|
|
281
277
|
|
|
@@ -283,13 +279,15 @@ const result = await compare('img1.png', 'img2.png', {
|
|
|
283
279
|
|
|
284
280
|
Work with in-memory images from screenshots or API responses.
|
|
285
281
|
|
|
282
|
+
For large screenshots, prefer file paths when possible. Buffer inputs are copied into the native worker before async processing begins, so path inputs put less pressure on the Node.js event loop and heap.
|
|
283
|
+
|
|
286
284
|
```javascript
|
|
287
|
-
|
|
285
|
+
import fs from 'node:fs';
|
|
288
286
|
|
|
289
287
|
// Compare buffers directly
|
|
290
288
|
let img1 = fs.readFileSync('image1.png');
|
|
291
289
|
let img2 = fs.readFileSync('image2.png');
|
|
292
|
-
let result = await compare(img1, img2, {
|
|
290
|
+
let result = await compare(img1, img2, { threshold: 5 });
|
|
293
291
|
|
|
294
292
|
// Mix paths and buffers
|
|
295
293
|
let screenshot = await page.screenshot(); // Returns Buffer
|
|
@@ -301,7 +299,7 @@ let result = await compare('baseline.png', screenshot);
|
|
|
301
299
|
Fast utility to get image dimensions without loading full image data.
|
|
302
300
|
|
|
303
301
|
```javascript
|
|
304
|
-
|
|
302
|
+
import { getDimensions } from '@vizzly-testing/honeydiff';
|
|
305
303
|
|
|
306
304
|
let dims = await getDimensions('screenshot.png');
|
|
307
305
|
console.log(`Image is ${dims.width}x${dims.height} pixels`);
|
|
@@ -317,7 +315,7 @@ let dims = await getDimensions(buffer);
|
|
|
317
315
|
Get image dimensions, file size, and format detection - useful for storage tracking and billing.
|
|
318
316
|
|
|
319
317
|
```javascript
|
|
320
|
-
|
|
318
|
+
import { getImageMetadata, getImageMetadataFromFile } from '@vizzly-testing/honeydiff';
|
|
321
319
|
|
|
322
320
|
// From buffer
|
|
323
321
|
let buffer = fs.readFileSync('screenshot.png');
|
|
@@ -339,7 +337,12 @@ console.log(`${metadata.width}x${metadata.height}, ${metadata.fileSizeBytes} byt
|
|
|
339
337
|
Group similar diffs across comparisons with fingerprinting. Perfect for batch-approving the same visual change that appears on multiple pages (e.g., header/footer updates).
|
|
340
338
|
|
|
341
339
|
```javascript
|
|
342
|
-
|
|
340
|
+
import {
|
|
341
|
+
compare,
|
|
342
|
+
computeFingerprintSync,
|
|
343
|
+
fingerprintSimilaritySync,
|
|
344
|
+
fingerprintHashSync
|
|
345
|
+
} from '@vizzly-testing/honeydiff';
|
|
343
346
|
|
|
344
347
|
// Compare images with clustering enabled
|
|
345
348
|
let result = await compare('baseline.png', 'current.png', {
|
|
@@ -406,7 +409,7 @@ Built-in WCAG color contrast analysis and color blindness simulation to catch ac
|
|
|
406
409
|
Analyze entire images for WCAG color contrast violations - perfect for catching accessibility issues in screenshots and UI designs.
|
|
407
410
|
|
|
408
411
|
```javascript
|
|
409
|
-
|
|
412
|
+
import { analyzeWcagContrast, saveWcagOverlay } from '@vizzly-testing/honeydiff';
|
|
410
413
|
|
|
411
414
|
// Analyze a screenshot for accessibility violations
|
|
412
415
|
let analysis = await analyzeWcagContrast('screenshot.png', {
|
|
@@ -461,12 +464,12 @@ Simulate how your UI appears to users with color vision deficiencies. Uses the s
|
|
|
461
464
|
- `achromatopsia` - Complete color blindness (grayscale only)
|
|
462
465
|
|
|
463
466
|
```javascript
|
|
464
|
-
|
|
467
|
+
import {
|
|
465
468
|
simulateColorBlindness,
|
|
466
469
|
saveColorBlindnessSimulation,
|
|
467
470
|
saveAllColorBlindnessSimulations,
|
|
468
471
|
getColorBlindnessTypes
|
|
469
|
-
}
|
|
472
|
+
} from '@vizzly-testing/honeydiff';
|
|
470
473
|
|
|
471
474
|
// Get CVD type information
|
|
472
475
|
let types = getColorBlindnessTypes();
|
|
@@ -492,7 +495,7 @@ await saveAllColorBlindnessSimulations('dashboard.png', 'dashboard', 'png');
|
|
|
492
495
|
Analyze contrast accessibility specifically for colorblind users. This catches issues that only appear when colors are perceived differently.
|
|
493
496
|
|
|
494
497
|
```javascript
|
|
495
|
-
|
|
498
|
+
import { analyzeWcagForCvd, analyzeWcagAllCvd } from '@vizzly-testing/honeydiff';
|
|
496
499
|
|
|
497
500
|
// Analyze for a specific CVD type
|
|
498
501
|
let analysis = await analyzeWcagForCvd('ui.png', 'deuteranopia', {
|
|
@@ -668,9 +671,8 @@ Benchmarked on 18.3M pixel screenshots (750 x 24,162 px):
|
|
|
668
671
|
|
|
669
672
|
| Operation | Time | Throughput |
|
|
670
673
|
|-----------|------|------------|
|
|
671
|
-
| Basic
|
|
674
|
+
| Basic CIEDE2000 comparison | ~79ms | 231.8M pixels/sec |
|
|
672
675
|
| With anti-aliasing | ~88ms | 208M pixels/sec |
|
|
673
|
-
| YIQ color space | ~89ms | 206M pixels/sec |
|
|
674
676
|
| With artifacts (diff/mask/overlay) | ~432ms | 42.4M pixels/sec |
|
|
675
677
|
|
|
676
678
|
**vs odiff:** 6.0x faster (79ms vs 474ms)
|
|
@@ -684,11 +686,11 @@ Multi-core parallelization: 215-309% CPU utilization across cores.
|
|
|
684
686
|
### CI/CD Integration
|
|
685
687
|
|
|
686
688
|
```javascript
|
|
687
|
-
|
|
689
|
+
import { compare } from '@vizzly-testing/honeydiff';
|
|
688
690
|
|
|
689
691
|
async function visualRegressionTest() {
|
|
690
692
|
let result = await compare('baseline.png', 'current.png', {
|
|
691
|
-
|
|
693
|
+
threshold: 10,
|
|
692
694
|
maxDiffs: 1000, // Early exit for fast failure
|
|
693
695
|
diffPath: process.env.CI ? './artifacts/diff.png' : undefined
|
|
694
696
|
});
|
|
@@ -721,7 +723,7 @@ if (await quickCompare('baseline.png', 'current.png')) {
|
|
|
721
723
|
|
|
722
724
|
```javascript
|
|
723
725
|
const result = await compare('baseline.png', 'current.png', {
|
|
724
|
-
|
|
726
|
+
threshold: 10
|
|
725
727
|
});
|
|
726
728
|
|
|
727
729
|
if (result.diffPercentage > 5.0) {
|
|
@@ -753,7 +755,7 @@ if (result.diffPercentage > 5.0) {
|
|
|
753
755
|
|
|
754
756
|
**Anti-aliasing too aggressive**
|
|
755
757
|
- Set `antialiasing: false` for stricter pixel matching
|
|
756
|
-
- Adjust `
|
|
758
|
+
- Adjust `threshold` instead for controlled flexibility
|
|
757
759
|
|
|
758
760
|
## Use Cases
|
|
759
761
|
|
package/examples/basic.js
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Basic usage example for @honeydiff
|
|
2
|
+
* Basic usage example for @vizzly-testing/honeydiff
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as honeydiff from '../index.js';
|
|
6
6
|
|
|
7
|
+
let baseline = '../../tests/fixtures/screenshots/vizzly-baseline.png';
|
|
8
|
+
let current = '../../tests/fixtures/screenshots/vizzly-with-diff.png';
|
|
9
|
+
|
|
7
10
|
async function main() {
|
|
8
|
-
console.log('Honeydiff Node.js
|
|
11
|
+
console.log('Honeydiff Node.js examples\n');
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let isDifferent = await honeydiff.quickCompare(
|
|
13
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
14
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png'
|
|
15
|
-
);
|
|
13
|
+
console.log('1. Quick comparison');
|
|
14
|
+
let isDifferent = await honeydiff.quickCompare(baseline, current);
|
|
16
15
|
console.log(` Result: ${isDifferent ? 'DIFFERENT' : 'IDENTICAL'}\n`);
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
);
|
|
17
|
+
console.log('2. Detailed comparison');
|
|
18
|
+
let result = await honeydiff.compare(baseline, current, {
|
|
19
|
+
threshold: 0,
|
|
20
|
+
antialiasing: true,
|
|
21
|
+
includeSSIM: true,
|
|
22
|
+
includeGMSD: true,
|
|
23
|
+
includeClusters: true,
|
|
24
|
+
includeDiffPixels: true,
|
|
25
|
+
minClusterSize: 1,
|
|
26
|
+
});
|
|
30
27
|
|
|
31
28
|
console.log(` Diff percentage: ${result.diffPercentage.toFixed(2)}%`);
|
|
32
29
|
console.log(` Different pixels: ${result.diffPixels} / ${result.totalPixels}`);
|
|
@@ -36,6 +33,10 @@ async function main() {
|
|
|
36
33
|
console.log(` SSIM score: ${result.perceptualScore.toFixed(4)}`);
|
|
37
34
|
}
|
|
38
35
|
|
|
36
|
+
if (result.gmsdScore !== null) {
|
|
37
|
+
console.log(` GMSD score: ${result.gmsdScore.toFixed(4)}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
39
40
|
if (result.boundingBox) {
|
|
40
41
|
console.log(
|
|
41
42
|
` Bounding box: (${result.boundingBox.x}, ${result.boundingBox.y}) ${result.boundingBox.width}x${result.boundingBox.height}`
|
|
@@ -44,270 +45,34 @@ async function main() {
|
|
|
44
45
|
|
|
45
46
|
if (result.diffClusters && result.diffClusters.length > 0) {
|
|
46
47
|
console.log(` Found ${result.diffClusters.length} diff clusters`);
|
|
47
|
-
result.diffClusters.slice(0, 3).
|
|
48
|
+
for (let [index, cluster] of result.diffClusters.slice(0, 3).entries()) {
|
|
48
49
|
console.log(
|
|
49
|
-
` Cluster ${
|
|
50
|
+
` Cluster ${index + 1}: ${cluster.pixelCount} pixels at (${cluster.centerOfMass[0].toFixed(0)}, ${cluster.centerOfMass[1].toFixed(0)})`
|
|
50
51
|
);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (result.intensityStats) {
|
|
55
|
-
console.log(` Intensity stats:`);
|
|
56
|
-
console.log(` Range: ${result.intensityStats.min}-${result.intensityStats.max}`);
|
|
57
|
-
console.log(
|
|
58
|
-
` Mean: ${result.intensityStats.mean.toFixed(1)}, Median: ${result.intensityStats.median}`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
console.log();
|
|
63
|
-
|
|
64
|
-
// Example 3: Synchronous comparison (for when you need blocking behavior)
|
|
65
|
-
console.log('3. Synchronous comparison:');
|
|
66
|
-
let syncResult = honeydiff.compareSync(
|
|
67
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
68
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
69
|
-
{ pixelTolerance: 10 }
|
|
70
|
-
);
|
|
71
|
-
console.log(` Diff percentage: ${syncResult.diffPercentage.toFixed(2)}%`);
|
|
72
|
-
console.log();
|
|
73
|
-
|
|
74
|
-
// Example 4: Get image dimensions
|
|
75
|
-
console.log('4. Get image dimensions:');
|
|
76
|
-
let dimensions = await honeydiff.getDimensions(
|
|
77
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png'
|
|
78
|
-
);
|
|
79
|
-
console.log(` Dimensions: ${dimensions.width}x${dimensions.height} pixels`);
|
|
80
|
-
console.log();
|
|
81
|
-
|
|
82
|
-
// Example 5: Get dimensions synchronously
|
|
83
|
-
console.log('5. Get dimensions (sync):');
|
|
84
|
-
let syncDimensions = honeydiff.getDimensionsSync(
|
|
85
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png'
|
|
86
|
-
);
|
|
87
|
-
console.log(` Dimensions: ${syncDimensions.width}x${syncDimensions.height} pixels`);
|
|
88
|
-
console.log();
|
|
89
|
-
|
|
90
|
-
// Example 6: Generate diff artifacts
|
|
91
|
-
console.log('6. Generate diff artifacts:');
|
|
92
|
-
let artifactResult = await honeydiff.compare(
|
|
93
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
94
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
95
|
-
{
|
|
96
|
-
diffPath: '/tmp/honeydiff-diff.png',
|
|
97
|
-
maskPath: '/tmp/honeydiff-mask.png',
|
|
98
|
-
overlayPath: '/tmp/honeydiff-overlay.png',
|
|
99
|
-
overwrite: true,
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
console.log(` Diff percentage: ${artifactResult.diffPercentage.toFixed(2)}%`);
|
|
103
|
-
console.log(` Artifacts saved to /tmp/honeydiff-*.png`);
|
|
104
|
-
console.log();
|
|
105
|
-
|
|
106
|
-
// Example 7: Accessibility metadata tests
|
|
107
|
-
console.log('7. Accessibility metadata (RGB + luminance + WCAG):');
|
|
108
|
-
let accessibilityResult = await honeydiff.compare(
|
|
109
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
110
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
111
|
-
{
|
|
112
|
-
includeClusters: true,
|
|
113
|
-
includeAccessibilityData: true,
|
|
114
|
-
}
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// Validate accessibility data is present
|
|
118
|
-
if (!accessibilityResult.diffClusters || accessibilityResult.diffClusters.length === 0) {
|
|
119
|
-
throw new Error('Test failed: Expected diff clusters to be present');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let firstCluster = accessibilityResult.diffClusters[0];
|
|
123
|
-
if (!firstCluster.accessibilityMetadata) {
|
|
124
|
-
throw new Error('Test failed: Expected accessibilityMetadata to be present');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
let meta = firstCluster.accessibilityMetadata;
|
|
128
|
-
|
|
129
|
-
// Validate RGB channel differences
|
|
130
|
-
if (typeof meta.avgRDiff !== 'number' || meta.avgRDiff < 0) {
|
|
131
|
-
throw new Error('Test failed: avgRDiff should be a non-negative number');
|
|
132
|
-
}
|
|
133
|
-
if (typeof meta.avgGDiff !== 'number' || meta.avgGDiff < 0) {
|
|
134
|
-
throw new Error('Test failed: avgGDiff should be a non-negative number');
|
|
135
|
-
}
|
|
136
|
-
if (typeof meta.avgBDiff !== 'number' || meta.avgBDiff < 0) {
|
|
137
|
-
throw new Error('Test failed: avgBDiff should be a non-negative number');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Validate dominant channel
|
|
141
|
-
if (!['R', 'G', 'B'].includes(meta.dominantChannel)) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`Test failed: dominantChannel should be R, G, or B, got ${meta.dominantChannel}`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Validate luminance values (0.0-1.0 range)
|
|
148
|
-
if (meta.avgBaselineLuminance < 0 || meta.avgBaselineLuminance > 1) {
|
|
149
|
-
throw new Error('Test failed: avgBaselineLuminance should be between 0 and 1');
|
|
150
|
-
}
|
|
151
|
-
if (meta.avgCurrentLuminance < 0 || meta.avgCurrentLuminance > 1) {
|
|
152
|
-
throw new Error('Test failed: avgCurrentLuminance should be between 0 and 1');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Validate contrast ratios (1.0-21.0 range)
|
|
156
|
-
if (meta.contrastRatio < 1 || meta.contrastRatio > 21) {
|
|
157
|
-
throw new Error('Test failed: contrastRatio should be between 1 and 21');
|
|
158
|
-
}
|
|
159
|
-
if (meta.minContrastRatio < 1 || meta.minContrastRatio > 21) {
|
|
160
|
-
throw new Error('Test failed: minContrastRatio should be between 1 and 21');
|
|
161
|
-
}
|
|
162
|
-
if (meta.maxContrastRatio < 1 || meta.maxContrastRatio > 21) {
|
|
163
|
-
throw new Error('Test failed: maxContrastRatio should be between 1 and 21');
|
|
164
|
-
}
|
|
165
|
-
if (meta.minContrastRatio > meta.maxContrastRatio) {
|
|
166
|
-
throw new Error('Test failed: minContrastRatio should be <= maxContrastRatio');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Validate colorBlindnessImpact is null when not requested
|
|
170
|
-
if (meta.colorBlindnessImpact !== null) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
'Test failed: colorBlindnessImpact should be null when checkColorBlindness is not enabled'
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.log(
|
|
177
|
-
` RGB diffs: R=${meta.avgRDiff.toFixed(1)}, G=${meta.avgGDiff.toFixed(1)}, B=${meta.avgBDiff.toFixed(1)}`
|
|
178
|
-
);
|
|
179
|
-
console.log(` Dominant channel: ${meta.dominantChannel}`);
|
|
180
|
-
console.log(
|
|
181
|
-
` Luminance: baseline=${meta.avgBaselineLuminance.toFixed(3)}, current=${meta.avgCurrentLuminance.toFixed(3)}`
|
|
182
|
-
);
|
|
183
|
-
console.log(
|
|
184
|
-
` WCAG contrast: ${meta.contrastRatio.toFixed(2)}:1 (range: ${meta.minContrastRatio.toFixed(2)}-${meta.maxContrastRatio.toFixed(2)})`
|
|
185
|
-
);
|
|
186
|
-
console.log(' All accessibility metadata tests passed!');
|
|
187
|
-
console.log();
|
|
188
|
-
|
|
189
|
-
// Example 8: Color blindness checking tests
|
|
190
|
-
console.log('8. Color blindness checking:');
|
|
191
|
-
let cbResult = await honeydiff.compare(
|
|
192
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
193
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
194
|
-
{
|
|
195
|
-
includeClusters: true,
|
|
196
|
-
checkColorBlindness: true,
|
|
197
|
-
colorBlindnessThreshold: 30.0,
|
|
198
52
|
}
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
// Validate clusters and accessibility data
|
|
202
|
-
if (!cbResult.diffClusters || cbResult.diffClusters.length === 0) {
|
|
203
|
-
throw new Error('Test failed: Expected diff clusters to be present');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let cbCluster = cbResult.diffClusters[0];
|
|
207
|
-
if (!cbCluster.accessibilityMetadata) {
|
|
208
|
-
throw new Error(
|
|
209
|
-
'Test failed: Expected accessibilityMetadata to be present when checkColorBlindness is enabled'
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
let cbMeta = cbCluster.accessibilityMetadata;
|
|
214
|
-
|
|
215
|
-
// Validate colorBlindnessImpact is present
|
|
216
|
-
if (!cbMeta.colorBlindnessImpact) {
|
|
217
|
-
throw new Error(
|
|
218
|
-
'Test failed: Expected colorBlindnessImpact to be present when checkColorBlindness is enabled'
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let impact = cbMeta.colorBlindnessImpact;
|
|
223
|
-
|
|
224
|
-
// Validate boolean flags
|
|
225
|
-
if (typeof impact.deuteranopiaVisible !== 'boolean') {
|
|
226
|
-
throw new Error('Test failed: deuteranopiaVisible should be a boolean');
|
|
227
|
-
}
|
|
228
|
-
if (typeof impact.protanopiaVisible !== 'boolean') {
|
|
229
|
-
throw new Error('Test failed: protanopiaVisible should be a boolean');
|
|
230
|
-
}
|
|
231
|
-
if (typeof impact.tritanopiaVisible !== 'boolean') {
|
|
232
|
-
throw new Error('Test failed: tritanopiaVisible should be a boolean');
|
|
233
|
-
}
|
|
234
|
-
if (typeof impact.achromatopsiaVisible !== 'boolean') {
|
|
235
|
-
throw new Error('Test failed: achromatopsiaVisible should be a boolean');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Validate visibility score (0.0-1.0 range)
|
|
239
|
-
if (impact.visibilityScore < 0 || impact.visibilityScore > 1) {
|
|
240
|
-
throw new Error('Test failed: visibilityScore should be between 0 and 1');
|
|
241
53
|
}
|
|
242
54
|
|
|
243
|
-
console.log(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
console.log(
|
|
249
|
-
console.log();
|
|
250
|
-
|
|
251
|
-
// Example 9: Test checkColorBlindness automatically enables includeAccessibilityData
|
|
252
|
-
console.log('9. Auto-enable test (checkColorBlindness enables includeAccessibilityData):');
|
|
253
|
-
let autoEnableResult = await honeydiff.compare(
|
|
254
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
255
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
256
|
-
{
|
|
257
|
-
includeClusters: true,
|
|
258
|
-
checkColorBlindness: true,
|
|
259
|
-
// Note: NOT explicitly setting includeAccessibilityData
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
if (!autoEnableResult.diffClusters || autoEnableResult.diffClusters.length === 0) {
|
|
264
|
-
throw new Error('Test failed: Expected diff clusters to be present');
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
let autoCluster = autoEnableResult.diffClusters[0];
|
|
268
|
-
if (!autoCluster.accessibilityMetadata) {
|
|
269
|
-
throw new Error(
|
|
270
|
-
'Test failed: checkColorBlindness should automatically enable accessibilityMetadata'
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Should have both accessibility data and color blindness impact
|
|
275
|
-
if (!autoCluster.accessibilityMetadata.colorBlindnessImpact) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
'Test failed: Expected colorBlindnessImpact when checkColorBlindness is enabled'
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
console.log(' Accessibility metadata auto-enabled: Yes');
|
|
282
|
-
console.log(' Color blindness data present: Yes');
|
|
283
|
-
console.log(' Auto-enable test passed!');
|
|
284
|
-
console.log();
|
|
285
|
-
|
|
286
|
-
// Example 10: Test accessibility data is null when not requested
|
|
287
|
-
console.log('10. Null handling test (accessibility data absent when not requested):');
|
|
288
|
-
let noAccessibilityResult = await honeydiff.compare(
|
|
289
|
-
'../../tests/fixtures/screenshots/vizzly-baseline.png',
|
|
290
|
-
'../../tests/fixtures/screenshots/vizzly-with-diff.png',
|
|
291
|
-
{
|
|
292
|
-
includeClusters: true,
|
|
293
|
-
// Note: NOT setting includeAccessibilityData or checkColorBlindness
|
|
294
|
-
}
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
if (!noAccessibilityResult.diffClusters || noAccessibilityResult.diffClusters.length === 0) {
|
|
298
|
-
throw new Error('Test failed: Expected diff clusters to be present');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
let noAccessCluster = noAccessibilityResult.diffClusters[0];
|
|
302
|
-
if (noAccessCluster.accessibilityMetadata !== null) {
|
|
303
|
-
throw new Error('Test failed: accessibilityMetadata should be null when not requested');
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
console.log(' Accessibility metadata: null (as expected)');
|
|
307
|
-
console.log(' Null handling test passed!');
|
|
308
|
-
console.log();
|
|
55
|
+
console.log('\n3. Synchronous comparison');
|
|
56
|
+
let syncResult = honeydiff.compareSync(baseline, current, {
|
|
57
|
+
threshold: 2,
|
|
58
|
+
includeClusters: true,
|
|
59
|
+
});
|
|
60
|
+
console.log(` Diff percentage: ${syncResult.diffPercentage.toFixed(2)}%`);
|
|
309
61
|
|
|
310
|
-
console.log('
|
|
62
|
+
console.log('\n4. Image dimensions');
|
|
63
|
+
let dimensions = await honeydiff.getDimensions(baseline);
|
|
64
|
+
console.log(` Dimensions: ${dimensions.width}x${dimensions.height}`);
|
|
65
|
+
|
|
66
|
+
console.log('\n5. Diff artifacts');
|
|
67
|
+
let artifactResult = await honeydiff.compare(baseline, current, {
|
|
68
|
+
threshold: 0,
|
|
69
|
+
diffPath: '/tmp/honeydiff-diff.png',
|
|
70
|
+
maskPath: '/tmp/honeydiff-mask.png',
|
|
71
|
+
overlayPath: '/tmp/honeydiff-side-by-side.png',
|
|
72
|
+
overwrite: true,
|
|
73
|
+
});
|
|
74
|
+
console.log(` Artifact diff percentage: ${artifactResult.diffPercentage.toFixed(2)}%`);
|
|
75
|
+
console.log(' Artifacts saved to /tmp/honeydiff-*.png');
|
|
311
76
|
}
|
|
312
77
|
|
|
313
78
|
main().catch((err) => {
|
package/examples/buffers.js
CHANGED
package/examples/typescript.ts
CHANGED
package/examples/wcag.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { analyzeWcagContrast, saveWcagOverlay } from '../index.js';
|
|
10
|
+
import { analyzeWcagContrast, analyzeWcagContrastSync, saveWcagOverlay } from '../index.js';
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
|
|
@@ -102,8 +102,6 @@ async function main() {
|
|
|
102
102
|
// Example 4: Synchronous API (for simple scripts)
|
|
103
103
|
console.log('4. Using synchronous API...');
|
|
104
104
|
try {
|
|
105
|
-
const { analyzeWcagContrastSync } = require('../index.js');
|
|
106
|
-
|
|
107
105
|
let analysis = analyzeWcagContrastSync(
|
|
108
106
|
path.join(__dirname, '../../../tests/fixtures/screenshots/vizzly-baseline.png'),
|
|
109
107
|
{
|
package/index.d.ts
CHANGED
|
@@ -187,7 +187,6 @@ export interface CompareOptions {
|
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
189
|
* Include clustered groups of connected differing pixels
|
|
190
|
-
* Automatically enables includeDiffPixels if not already enabled
|
|
191
190
|
* Clusters are sorted by size (largest first)
|
|
192
191
|
* @default false
|
|
193
192
|
*/
|
|
@@ -316,6 +315,8 @@ export type ImageInput = string | Buffer;
|
|
|
316
315
|
|
|
317
316
|
/**
|
|
318
317
|
* Compare two images asynchronously (recommended)
|
|
318
|
+
* For large screenshots, prefer file path inputs. Buffer inputs are copied
|
|
319
|
+
* before native worker processing begins.
|
|
319
320
|
*
|
|
320
321
|
* @param img1 - First image (file path or Buffer)
|
|
321
322
|
* @param img2 - Second image (file path or Buffer)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/honeydiff",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "High-performance image diffing for Node.js - Native bindings to Honeydiff Rust library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/vizzly-testing/honeydiff"
|
|
31
|
+
"url": "https://github.com/vizzly-testing/r-honeydiff"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"image",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|