laxy-verify 1.2.0 → 1.2.1
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/audit/broken-links.d.ts +21 -21
- package/dist/audit/broken-links.js +86 -86
- package/dist/auth.d.ts +11 -11
- package/dist/auth.js +222 -222
- package/dist/cli.js +830 -806
- package/dist/comment.d.ts +21 -21
- package/dist/comment.js +125 -125
- package/dist/crawler.d.ts +36 -36
- package/dist/crawler.js +357 -357
- package/dist/e2e.d.ts +49 -49
- package/dist/e2e.js +565 -565
- package/dist/entitlement.d.ts +11 -11
- package/dist/entitlement.js +90 -90
- package/dist/init.js +87 -87
- package/dist/multi-viewport.d.ts +31 -31
- package/dist/multi-viewport.js +298 -298
- package/dist/playwright-runner.d.ts +16 -16
- package/dist/playwright-runner.js +208 -208
- package/dist/report-markdown.d.ts +39 -39
- package/dist/report-markdown.js +386 -386
- package/dist/security-audit.d.ts +9 -9
- package/dist/security-audit.js +64 -64
- package/dist/serve.d.ts +13 -13
- package/dist/serve.js +196 -196
- package/dist/trend.d.ts +50 -50
- package/dist/trend.js +148 -148
- package/dist/verification-core/index.d.ts +3 -3
- package/dist/verification-core/index.js +19 -19
- package/dist/verification-core/report.d.ts +14 -14
- package/dist/verification-core/report.js +409 -409
- package/dist/verification-core/tier-policy.d.ts +13 -13
- package/dist/verification-core/tier-policy.js +60 -60
- package/dist/verification-core/types.d.ts +108 -108
- package/dist/verification-core/types.js +2 -2
- package/dist/visual-diff.d.ts +26 -26
- package/dist/visual-diff.js +178 -178
- package/package.json +1 -1
package/dist/visual-diff.js
CHANGED
|
@@ -1,178 +1,178 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.formatVisualDiffSummary = formatVisualDiffSummary;
|
|
40
|
-
exports.runVisualDiff = runVisualDiff;
|
|
41
|
-
const fs = __importStar(require("node:fs"));
|
|
42
|
-
const path = __importStar(require("node:path"));
|
|
43
|
-
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
44
|
-
const pngjs_1 = require("pngjs");
|
|
45
|
-
const VISUAL_DIFF_VIEWPORTS = [
|
|
46
|
-
{ viewport: "desktop", width: 1280, height: 720 },
|
|
47
|
-
{ viewport: "tablet", width: 768, height: 1024 },
|
|
48
|
-
{ viewport: "mobile", width: 375, height: 812, isMobile: true, deviceScaleFactor: 2 },
|
|
49
|
-
];
|
|
50
|
-
function formatViewportResult(viewport) {
|
|
51
|
-
return viewport.hasBaseline
|
|
52
|
-
? `${viewport.viewport} ${viewport.diffPercentage}% (${viewport.verdict})`
|
|
53
|
-
: `${viewport.viewport} baseline seeded`;
|
|
54
|
-
}
|
|
55
|
-
function formatVisualDiffSummary(result) {
|
|
56
|
-
return result.viewports.map(formatViewportResult).join(", ");
|
|
57
|
-
}
|
|
58
|
-
function ensureDir(dir) {
|
|
59
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
60
|
-
}
|
|
61
|
-
async function captureScreenshot(url, outputPath, viewport) {
|
|
62
|
-
const browser = await puppeteer_1.default.launch({ headless: true, args: ["--no-sandbox", "--disable-setuid-sandbox"] });
|
|
63
|
-
try {
|
|
64
|
-
const page = await browser.newPage();
|
|
65
|
-
await page.setViewport({
|
|
66
|
-
width: viewport.width,
|
|
67
|
-
height: viewport.height,
|
|
68
|
-
isMobile: viewport.isMobile ?? false,
|
|
69
|
-
deviceScaleFactor: viewport.deviceScaleFactor ?? 1,
|
|
70
|
-
});
|
|
71
|
-
await page.goto(url, { waitUntil: "networkidle2", timeout: 20000 });
|
|
72
|
-
await page.waitForSelector("body", { timeout: 5000 });
|
|
73
|
-
await page.screenshot({ path: outputPath, fullPage: true, type: "png" });
|
|
74
|
-
}
|
|
75
|
-
finally {
|
|
76
|
-
await browser.close();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
async function loadPixelmatch() {
|
|
80
|
-
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
81
|
-
const mod = await dynamicImport("pixelmatch");
|
|
82
|
-
return mod.default;
|
|
83
|
-
}
|
|
84
|
-
async function compareImages(baselinePath, currentPath, diffOutputPath) {
|
|
85
|
-
const baselinePng = pngjs_1.PNG.sync.read(fs.readFileSync(baselinePath));
|
|
86
|
-
const currentPng = pngjs_1.PNG.sync.read(fs.readFileSync(currentPath));
|
|
87
|
-
const width = Math.min(baselinePng.width, currentPng.width);
|
|
88
|
-
const height = Math.min(baselinePng.height, currentPng.height);
|
|
89
|
-
const cropData = (png, w, h) => {
|
|
90
|
-
if (png.width === w && png.height === h)
|
|
91
|
-
return png.data;
|
|
92
|
-
const cropped = Buffer.alloc(w * h * 4);
|
|
93
|
-
for (let y = 0; y < h; y++) {
|
|
94
|
-
png.data.copy(cropped, y * w * 4, y * png.width * 4, y * png.width * 4 + w * 4);
|
|
95
|
-
}
|
|
96
|
-
return cropped;
|
|
97
|
-
};
|
|
98
|
-
const baseData = cropData(baselinePng, width, height);
|
|
99
|
-
const currData = cropData(currentPng, width, height);
|
|
100
|
-
const diff = new pngjs_1.PNG({ width, height });
|
|
101
|
-
const pixelmatch = await loadPixelmatch();
|
|
102
|
-
const diffPixels = pixelmatch(baseData, currData, diff.data, width, height, { threshold: 0.1 });
|
|
103
|
-
ensureDir(path.dirname(diffOutputPath));
|
|
104
|
-
fs.writeFileSync(diffOutputPath, pngjs_1.PNG.sync.write(diff));
|
|
105
|
-
const totalPixels = width * height;
|
|
106
|
-
const diffPercentage = Math.round((diffPixels / totalPixels) * 10000) / 100;
|
|
107
|
-
return { diffPixels, totalPixels, diffPercentage };
|
|
108
|
-
}
|
|
109
|
-
async function runVisualDiff(projectDir, url, label = "current") {
|
|
110
|
-
const dir = path.join(projectDir, ".laxy-verify", "visual");
|
|
111
|
-
ensureDir(dir);
|
|
112
|
-
const viewportResults = [];
|
|
113
|
-
for (const viewport of VISUAL_DIFF_VIEWPORTS) {
|
|
114
|
-
const baselinePath = path.join(dir, `${viewport.viewport}.baseline.png`);
|
|
115
|
-
const currentPath = path.join(dir, `${label}.${viewport.viewport}.png`);
|
|
116
|
-
const diffPath = path.join(dir, `${label}.${viewport.viewport}.diff.png`);
|
|
117
|
-
await captureScreenshot(url, currentPath, viewport);
|
|
118
|
-
if (!fs.existsSync(baselinePath)) {
|
|
119
|
-
fs.copyFileSync(currentPath, baselinePath);
|
|
120
|
-
viewportResults.push({
|
|
121
|
-
viewport: viewport.viewport,
|
|
122
|
-
hasBaseline: false,
|
|
123
|
-
diffPercentage: 0,
|
|
124
|
-
verdict: "pass",
|
|
125
|
-
diffPixels: 0,
|
|
126
|
-
totalPixels: 0,
|
|
127
|
-
baselinePath,
|
|
128
|
-
currentPath,
|
|
129
|
-
diffPath: "",
|
|
130
|
-
});
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
const comparison = await compareImages(baselinePath, currentPath, diffPath);
|
|
134
|
-
let verdict = "pass";
|
|
135
|
-
if (comparison.diffPercentage >= 60) {
|
|
136
|
-
verdict = "rollback";
|
|
137
|
-
}
|
|
138
|
-
else if (comparison.diffPercentage >= 30) {
|
|
139
|
-
verdict = "warn";
|
|
140
|
-
}
|
|
141
|
-
if (verdict === "pass") {
|
|
142
|
-
fs.copyFileSync(currentPath, baselinePath);
|
|
143
|
-
}
|
|
144
|
-
viewportResults.push({
|
|
145
|
-
viewport: viewport.viewport,
|
|
146
|
-
hasBaseline: true,
|
|
147
|
-
diffPercentage: comparison.diffPercentage,
|
|
148
|
-
verdict,
|
|
149
|
-
diffPixels: comparison.diffPixels,
|
|
150
|
-
totalPixels: comparison.totalPixels,
|
|
151
|
-
baselinePath,
|
|
152
|
-
currentPath,
|
|
153
|
-
diffPath,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
const comparableResults = viewportResults.filter((viewport) => viewport.hasBaseline);
|
|
157
|
-
const summary = viewportResults.map(formatViewportResult).join(", ");
|
|
158
|
-
const worstVerdict = comparableResults.some((viewport) => viewport.verdict === "rollback")
|
|
159
|
-
? "rollback"
|
|
160
|
-
: comparableResults.some((viewport) => viewport.verdict === "warn")
|
|
161
|
-
? "warn"
|
|
162
|
-
: "pass";
|
|
163
|
-
const maxDiffPercentage = comparableResults.reduce((max, viewport) => Math.max(max, viewport.diffPercentage), 0);
|
|
164
|
-
const totalDiffPixels = comparableResults.reduce((sum, viewport) => sum + viewport.diffPixels, 0);
|
|
165
|
-
const totalPixels = comparableResults.reduce((sum, viewport) => sum + viewport.totalPixels, 0);
|
|
166
|
-
return {
|
|
167
|
-
hasBaseline: comparableResults.length === viewportResults.length,
|
|
168
|
-
diffPercentage: Math.round(maxDiffPercentage * 100) / 100,
|
|
169
|
-
verdict: worstVerdict,
|
|
170
|
-
diffPixels: totalDiffPixels,
|
|
171
|
-
totalPixels,
|
|
172
|
-
baselinePath: viewportResults[0]?.baselinePath ?? "",
|
|
173
|
-
currentPath: viewportResults[0]?.currentPath ?? "",
|
|
174
|
-
diffPath: viewportResults[0]?.diffPath ?? "",
|
|
175
|
-
viewports: viewportResults,
|
|
176
|
-
summary,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.formatVisualDiffSummary = formatVisualDiffSummary;
|
|
40
|
+
exports.runVisualDiff = runVisualDiff;
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
44
|
+
const pngjs_1 = require("pngjs");
|
|
45
|
+
const VISUAL_DIFF_VIEWPORTS = [
|
|
46
|
+
{ viewport: "desktop", width: 1280, height: 720 },
|
|
47
|
+
{ viewport: "tablet", width: 768, height: 1024 },
|
|
48
|
+
{ viewport: "mobile", width: 375, height: 812, isMobile: true, deviceScaleFactor: 2 },
|
|
49
|
+
];
|
|
50
|
+
function formatViewportResult(viewport) {
|
|
51
|
+
return viewport.hasBaseline
|
|
52
|
+
? `${viewport.viewport} ${viewport.diffPercentage}% (${viewport.verdict})`
|
|
53
|
+
: `${viewport.viewport} baseline seeded`;
|
|
54
|
+
}
|
|
55
|
+
function formatVisualDiffSummary(result) {
|
|
56
|
+
return result.viewports.map(formatViewportResult).join(", ");
|
|
57
|
+
}
|
|
58
|
+
function ensureDir(dir) {
|
|
59
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
async function captureScreenshot(url, outputPath, viewport) {
|
|
62
|
+
const browser = await puppeteer_1.default.launch({ headless: true, args: ["--no-sandbox", "--disable-setuid-sandbox"] });
|
|
63
|
+
try {
|
|
64
|
+
const page = await browser.newPage();
|
|
65
|
+
await page.setViewport({
|
|
66
|
+
width: viewport.width,
|
|
67
|
+
height: viewport.height,
|
|
68
|
+
isMobile: viewport.isMobile ?? false,
|
|
69
|
+
deviceScaleFactor: viewport.deviceScaleFactor ?? 1,
|
|
70
|
+
});
|
|
71
|
+
await page.goto(url, { waitUntil: "networkidle2", timeout: 20000 });
|
|
72
|
+
await page.waitForSelector("body", { timeout: 5000 });
|
|
73
|
+
await page.screenshot({ path: outputPath, fullPage: true, type: "png" });
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await browser.close();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function loadPixelmatch() {
|
|
80
|
+
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
81
|
+
const mod = await dynamicImport("pixelmatch");
|
|
82
|
+
return mod.default;
|
|
83
|
+
}
|
|
84
|
+
async function compareImages(baselinePath, currentPath, diffOutputPath) {
|
|
85
|
+
const baselinePng = pngjs_1.PNG.sync.read(fs.readFileSync(baselinePath));
|
|
86
|
+
const currentPng = pngjs_1.PNG.sync.read(fs.readFileSync(currentPath));
|
|
87
|
+
const width = Math.min(baselinePng.width, currentPng.width);
|
|
88
|
+
const height = Math.min(baselinePng.height, currentPng.height);
|
|
89
|
+
const cropData = (png, w, h) => {
|
|
90
|
+
if (png.width === w && png.height === h)
|
|
91
|
+
return png.data;
|
|
92
|
+
const cropped = Buffer.alloc(w * h * 4);
|
|
93
|
+
for (let y = 0; y < h; y++) {
|
|
94
|
+
png.data.copy(cropped, y * w * 4, y * png.width * 4, y * png.width * 4 + w * 4);
|
|
95
|
+
}
|
|
96
|
+
return cropped;
|
|
97
|
+
};
|
|
98
|
+
const baseData = cropData(baselinePng, width, height);
|
|
99
|
+
const currData = cropData(currentPng, width, height);
|
|
100
|
+
const diff = new pngjs_1.PNG({ width, height });
|
|
101
|
+
const pixelmatch = await loadPixelmatch();
|
|
102
|
+
const diffPixels = pixelmatch(baseData, currData, diff.data, width, height, { threshold: 0.1 });
|
|
103
|
+
ensureDir(path.dirname(diffOutputPath));
|
|
104
|
+
fs.writeFileSync(diffOutputPath, pngjs_1.PNG.sync.write(diff));
|
|
105
|
+
const totalPixels = width * height;
|
|
106
|
+
const diffPercentage = Math.round((diffPixels / totalPixels) * 10000) / 100;
|
|
107
|
+
return { diffPixels, totalPixels, diffPercentage };
|
|
108
|
+
}
|
|
109
|
+
async function runVisualDiff(projectDir, url, label = "current") {
|
|
110
|
+
const dir = path.join(projectDir, ".laxy-verify", "visual");
|
|
111
|
+
ensureDir(dir);
|
|
112
|
+
const viewportResults = [];
|
|
113
|
+
for (const viewport of VISUAL_DIFF_VIEWPORTS) {
|
|
114
|
+
const baselinePath = path.join(dir, `${viewport.viewport}.baseline.png`);
|
|
115
|
+
const currentPath = path.join(dir, `${label}.${viewport.viewport}.png`);
|
|
116
|
+
const diffPath = path.join(dir, `${label}.${viewport.viewport}.diff.png`);
|
|
117
|
+
await captureScreenshot(url, currentPath, viewport);
|
|
118
|
+
if (!fs.existsSync(baselinePath)) {
|
|
119
|
+
fs.copyFileSync(currentPath, baselinePath);
|
|
120
|
+
viewportResults.push({
|
|
121
|
+
viewport: viewport.viewport,
|
|
122
|
+
hasBaseline: false,
|
|
123
|
+
diffPercentage: 0,
|
|
124
|
+
verdict: "pass",
|
|
125
|
+
diffPixels: 0,
|
|
126
|
+
totalPixels: 0,
|
|
127
|
+
baselinePath,
|
|
128
|
+
currentPath,
|
|
129
|
+
diffPath: "",
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const comparison = await compareImages(baselinePath, currentPath, diffPath);
|
|
134
|
+
let verdict = "pass";
|
|
135
|
+
if (comparison.diffPercentage >= 60) {
|
|
136
|
+
verdict = "rollback";
|
|
137
|
+
}
|
|
138
|
+
else if (comparison.diffPercentage >= 30) {
|
|
139
|
+
verdict = "warn";
|
|
140
|
+
}
|
|
141
|
+
if (verdict === "pass") {
|
|
142
|
+
fs.copyFileSync(currentPath, baselinePath);
|
|
143
|
+
}
|
|
144
|
+
viewportResults.push({
|
|
145
|
+
viewport: viewport.viewport,
|
|
146
|
+
hasBaseline: true,
|
|
147
|
+
diffPercentage: comparison.diffPercentage,
|
|
148
|
+
verdict,
|
|
149
|
+
diffPixels: comparison.diffPixels,
|
|
150
|
+
totalPixels: comparison.totalPixels,
|
|
151
|
+
baselinePath,
|
|
152
|
+
currentPath,
|
|
153
|
+
diffPath,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const comparableResults = viewportResults.filter((viewport) => viewport.hasBaseline);
|
|
157
|
+
const summary = viewportResults.map(formatViewportResult).join(", ");
|
|
158
|
+
const worstVerdict = comparableResults.some((viewport) => viewport.verdict === "rollback")
|
|
159
|
+
? "rollback"
|
|
160
|
+
: comparableResults.some((viewport) => viewport.verdict === "warn")
|
|
161
|
+
? "warn"
|
|
162
|
+
: "pass";
|
|
163
|
+
const maxDiffPercentage = comparableResults.reduce((max, viewport) => Math.max(max, viewport.diffPercentage), 0);
|
|
164
|
+
const totalDiffPixels = comparableResults.reduce((sum, viewport) => sum + viewport.diffPixels, 0);
|
|
165
|
+
const totalPixels = comparableResults.reduce((sum, viewport) => sum + viewport.totalPixels, 0);
|
|
166
|
+
return {
|
|
167
|
+
hasBaseline: comparableResults.length === viewportResults.length,
|
|
168
|
+
diffPercentage: Math.round(maxDiffPercentage * 100) / 100,
|
|
169
|
+
verdict: worstVerdict,
|
|
170
|
+
diffPixels: totalDiffPixels,
|
|
171
|
+
totalPixels,
|
|
172
|
+
baselinePath: viewportResults[0]?.baselinePath ?? "",
|
|
173
|
+
currentPath: viewportResults[0]?.currentPath ?? "",
|
|
174
|
+
diffPath: viewportResults[0]?.diffPath ?? "",
|
|
175
|
+
viewports: viewportResults,
|
|
176
|
+
summary,
|
|
177
|
+
};
|
|
178
|
+
}
|