@testivai/witness-playwright 1.3.3 → 1.4.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.
- package/dist/config/loader.js +2 -1
- package/dist/snapshot.js +69 -0
- package/dist/types.d.ts +12 -0
- package/package.json +2 -2
package/dist/config/loader.js
CHANGED
|
@@ -138,7 +138,8 @@ function mergeTestConfig(projectConfig, testConfig) {
|
|
|
138
138
|
// the selectors (masked for a long time by the diff engine's cumulated
|
|
139
139
|
// threshold absorbing the tiny leaked pixels).
|
|
140
140
|
ignoreSelectors: testConfig.ignoreSelectors,
|
|
141
|
-
stabilize: testConfig.stabilize
|
|
141
|
+
stabilize: testConfig.stabilize,
|
|
142
|
+
mask: testConfig.mask
|
|
142
143
|
};
|
|
143
144
|
}
|
|
144
145
|
/**
|
package/dist/snapshot.js
CHANGED
|
@@ -408,6 +408,61 @@ async function snapshot(page, testInfo, name, config) {
|
|
|
408
408
|
console.warn('[TestivAI] DOM capture failed (noise-hint will be unavailable):', err);
|
|
409
409
|
}
|
|
410
410
|
}
|
|
411
|
+
// Mask support: record per-call mask specs and capture geometry for
|
|
412
|
+
// every selector mask (config + per-call) via getBoundingClientRect.
|
|
413
|
+
// The DOM snapshot carries no layout, so comparison-time selector
|
|
414
|
+
// masks depend on these rects; when absent, the compare degrades to
|
|
415
|
+
// a visible warning. Best-effort — never breaks the capture.
|
|
416
|
+
try {
|
|
417
|
+
const perCallMasks = effectiveConfig.mask ?? [];
|
|
418
|
+
const configMaskEntries = readConfigMasks(process.cwd());
|
|
419
|
+
const selectorMasks = [
|
|
420
|
+
...configMaskEntries.filter((m) => typeof m === 'string'),
|
|
421
|
+
...perCallMasks.filter((m) => typeof m === 'string'),
|
|
422
|
+
];
|
|
423
|
+
const uniqueSelectors = [...new Set(selectorMasks)];
|
|
424
|
+
let maskRects = [];
|
|
425
|
+
if (uniqueSelectors.length > 0) {
|
|
426
|
+
maskRects = await page.evaluate((selectors) => {
|
|
427
|
+
const dpr = window.devicePixelRatio || 1;
|
|
428
|
+
const out = [];
|
|
429
|
+
for (const sel of selectors) {
|
|
430
|
+
try {
|
|
431
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
432
|
+
const r = el.getBoundingClientRect();
|
|
433
|
+
if (r.width > 0 && r.height > 0) {
|
|
434
|
+
out.push({
|
|
435
|
+
selector: sel,
|
|
436
|
+
x: Math.round((r.x + window.scrollX) * dpr),
|
|
437
|
+
y: Math.round((r.y + window.scrollY) * dpr),
|
|
438
|
+
width: Math.round(r.width * dpr),
|
|
439
|
+
height: Math.round(r.height * dpr),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// invalid selector — the compare side will warn
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return out;
|
|
449
|
+
}, uniqueSelectors);
|
|
450
|
+
}
|
|
451
|
+
const geometricCallMasks = perCallMasks.filter((m) => typeof m !== 'string');
|
|
452
|
+
const callSelectorMasks = perCallMasks.filter((m) => typeof m === 'string');
|
|
453
|
+
if (geometricCallMasks.length > 0 || callSelectorMasks.length > 0 || maskRects.length > 0) {
|
|
454
|
+
await fs.writeJson(path.join(localSnapshotDir, 'metadata.json'), {
|
|
455
|
+
name: snapshotName,
|
|
456
|
+
timestamp: new Date(timestamp).toISOString(),
|
|
457
|
+
...(geometricCallMasks.length > 0 ? { masks: geometricCallMasks } : {}),
|
|
458
|
+
...(callSelectorMasks.length > 0 ? { maskSelectors: callSelectorMasks } : {}),
|
|
459
|
+
...(maskRects.length > 0 ? { maskRects } : {}),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
// metadata is an enhancement; the screenshot path never depends on it
|
|
465
|
+
}
|
|
411
466
|
}
|
|
412
467
|
// 2. Dump page structure (HTML) - skip in local mode
|
|
413
468
|
// @renamed: domPath → structurePath (IP protection)
|
|
@@ -709,3 +764,17 @@ async function snapshot(page, testInfo, name, config) {
|
|
|
709
764
|
structureAnalysis
|
|
710
765
|
});
|
|
711
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Read the global `mask` list from .testivai/config.json (selector strings
|
|
769
|
+
* and geometric objects). Returns [] when absent or malformed.
|
|
770
|
+
*/
|
|
771
|
+
function readConfigMasks(projectRoot) {
|
|
772
|
+
try {
|
|
773
|
+
const raw = fs.readFileSync(path.join(projectRoot, '.testivai', 'config.json'), 'utf-8');
|
|
774
|
+
const parsed = JSON.parse(raw);
|
|
775
|
+
return Array.isArray(parsed?.mask) ? parsed.mask : [];
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
return [];
|
|
779
|
+
}
|
|
780
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -239,6 +239,18 @@ export interface TestivAIConfig {
|
|
|
239
239
|
* or in `testivai.config.ts`. Per-snapshot values win.
|
|
240
240
|
*/
|
|
241
241
|
stabilize?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Masks for this snapshot: areas excluded from the pixel diff and
|
|
244
|
+
* hatched in the report (auditable — never silent). Each entry is a
|
|
245
|
+
* CSS selector string (geometry recorded at capture time) or a
|
|
246
|
+
* geometric region — px numbers, 0–1 ratios, "NN%" strings, or a
|
|
247
|
+
* single-edge shorthand like { top: 24 }.
|
|
248
|
+
*
|
|
249
|
+
* Merged with the global `mask` list from `.testivai/config.json`.
|
|
250
|
+
* Unlike `ignoreSelectors` (which hides elements during capture),
|
|
251
|
+
* masks act at comparison time and stay visible in the diff output.
|
|
252
|
+
*/
|
|
253
|
+
mask?: Array<string | Record<string, number | string>>;
|
|
242
254
|
}
|
|
243
255
|
/**
|
|
244
256
|
* Layout/Bounding box data for an element
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testivai/witness-playwright",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Playwright sensor for Testivai Visual Regression Test system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"sharp": "^0.34.5",
|
|
49
49
|
"simple-git": "^3.21.0",
|
|
50
50
|
"@testivai/common": "0.2.2",
|
|
51
|
-
"@testivai/witness": "1.
|
|
51
|
+
"@testivai/witness": "1.4.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@playwright/test": "^1.60.0",
|