@crvy/rprtr 0.0.4 → 0.0.5
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 +21 -0
- package/README.md +9 -0
- package/dist/{chunk-BRFA7JCQ.js → chunk-IH44LYNM.js} +138 -15
- package/dist/{chunk-IEYNAB6M.js → chunk-MWLM3HWS.js} +66 -16
- package/dist/cli.js +2 -2
- package/dist/index.css +52 -0
- package/dist/index.js +154 -83
- package/dist/report-state.d.ts.map +1 -1
- package/dist/report-utils.d.ts +3 -1
- package/dist/report-utils.d.ts.map +1 -1
- package/dist/reporter-utils.d.ts +11 -1
- package/dist/reporter-utils.d.ts.map +1 -1
- package/dist/reporter.cjs +138 -34
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +74 -20
- package/dist/schemas.d.ts +103 -8
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server/app.d.ts +1 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/report-watch.d.ts +9 -0
- package/dist/server/report-watch.d.ts.map +1 -0
- package/dist/server.cjs +205 -32
- package/dist/server.js +2 -2
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/server/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAUD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAChC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,aAAa,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClD,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3D;AA+KD,wBAAsB,eAAe,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CA4CrF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function createDebouncedRefresh(reload: () => Promise<void>, delayMs?: number): () => void;
|
|
2
|
+
interface ReportWatchOptions {
|
|
3
|
+
offlineReportDir: string;
|
|
4
|
+
screenshotDir: string;
|
|
5
|
+
scheduleRefresh: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function watchReportArtifacts(options: ReportWatchOptions): Promise<() => void>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=report-watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-watch.d.ts","sourceRoot":"","sources":["../../src/server/report-watch.ts"],"names":[],"mappings":"AAOA,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,SAAK,GAAG,MAAM,IAAI,CAa5F;AAED,UAAU,kBAAkB;IAC1B,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,IAAI,CAAA;CAC5B;AAsFD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAgC3F"}
|
package/dist/server.cjs
CHANGED
|
@@ -35,7 +35,7 @@ __export(server_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(server_exports);
|
|
36
36
|
|
|
37
37
|
// src/server/app.ts
|
|
38
|
-
var
|
|
38
|
+
var import_path5 = require("path");
|
|
39
39
|
var import_url = require("url");
|
|
40
40
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
41
41
|
|
|
@@ -47,6 +47,30 @@ var import_path = require("path");
|
|
|
47
47
|
function normalizeScreenshotsBaseUrl(baseUrl) {
|
|
48
48
|
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
49
49
|
}
|
|
50
|
+
function classifyImage(image) {
|
|
51
|
+
if (image.actual !== void 0 || image.diff !== void 0) {
|
|
52
|
+
return "comparison";
|
|
53
|
+
}
|
|
54
|
+
if (image.expect !== void 0) {
|
|
55
|
+
return "baseline-only";
|
|
56
|
+
}
|
|
57
|
+
return "declared-only";
|
|
58
|
+
}
|
|
59
|
+
function withImageSource(image) {
|
|
60
|
+
return {
|
|
61
|
+
...image,
|
|
62
|
+
source: classifyImage(image)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function mergeDeclaredImages(images, visualNames) {
|
|
66
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(images), ...visualNames]);
|
|
67
|
+
return Object.fromEntries(
|
|
68
|
+
Array.from(names, (name) => {
|
|
69
|
+
const current = images[name] ?? {};
|
|
70
|
+
return [name, withImageSource(current)];
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
}
|
|
50
74
|
function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/") {
|
|
51
75
|
const images = {};
|
|
52
76
|
const baseUrl = normalizeScreenshotsBaseUrl(screenshotsBaseUrl);
|
|
@@ -57,7 +81,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
57
81
|
const baseName = match[1];
|
|
58
82
|
const role = match[2];
|
|
59
83
|
if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
|
|
60
|
-
images[baseName] ??= {
|
|
84
|
+
images[baseName] ??= {};
|
|
61
85
|
const url = `${baseUrl}${attachment.path}`;
|
|
62
86
|
const img = images[baseName];
|
|
63
87
|
if (img !== null && img !== void 0) {
|
|
@@ -68,10 +92,11 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
68
92
|
}
|
|
69
93
|
for (const key of Object.keys(images)) {
|
|
70
94
|
const img = images[key];
|
|
71
|
-
if (img?.actual !==
|
|
95
|
+
if (img?.actual !== void 0 && img?.expect !== void 0 && img?.diff === void 0) {
|
|
72
96
|
delete img.expect;
|
|
97
|
+
}
|
|
73
98
|
}
|
|
74
|
-
return images;
|
|
99
|
+
return mergeDeclaredImages(images, []);
|
|
75
100
|
}
|
|
76
101
|
function mapStatus(status) {
|
|
77
102
|
switch (status) {
|
|
@@ -87,17 +112,39 @@ function mapStatus(status) {
|
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
// src/report-state.ts
|
|
90
|
-
function
|
|
91
|
-
return
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
function isCurrentArtifact(image) {
|
|
116
|
+
return image?.actual !== void 0 || image?.expect !== void 0 || image?.diff !== void 0;
|
|
117
|
+
}
|
|
118
|
+
function isReusablePassingImage(image) {
|
|
119
|
+
const source = image.source ?? classifyImage(image);
|
|
120
|
+
if (source === "comparison") {
|
|
121
|
+
return image.actual !== void 0 && image.diff === void 0;
|
|
122
|
+
}
|
|
123
|
+
return source === "baseline-only";
|
|
124
|
+
}
|
|
125
|
+
function hasReusablePassingImages(images) {
|
|
126
|
+
return Object.values(images).some((img) => img !== null && img !== void 0 && isReusablePassingImage(img));
|
|
94
127
|
}
|
|
95
128
|
function preservePreviousPassingImages(test, status, images) {
|
|
96
|
-
if (status !== "passed"
|
|
129
|
+
if (status !== "passed") {
|
|
97
130
|
return images;
|
|
98
131
|
}
|
|
99
132
|
const previousImages = test.results?.[0]?.images ?? {};
|
|
100
|
-
|
|
133
|
+
if (!hasReusablePassingImages(previousImages)) {
|
|
134
|
+
return images;
|
|
135
|
+
}
|
|
136
|
+
return Object.entries(previousImages).reduce((currentImages, [name, previousImage]) => {
|
|
137
|
+
if (!Object.hasOwn(currentImages, name) || previousImage === void 0 || !isReusablePassingImage(previousImage) || isCurrentArtifact(currentImages[name])) {
|
|
138
|
+
return currentImages;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
...currentImages,
|
|
142
|
+
[name]: {
|
|
143
|
+
...previousImage,
|
|
144
|
+
source: previousImage.source ?? classifyImage(previousImage)
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}, images);
|
|
101
148
|
}
|
|
102
149
|
function countDiffImages(images) {
|
|
103
150
|
return Object.values(images).filter((img) => img?.diff !== null && img?.diff !== void 0).length;
|
|
@@ -133,10 +180,11 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
133
180
|
return null;
|
|
134
181
|
}
|
|
135
182
|
test.status = mapStatus(data.status);
|
|
183
|
+
const resultStatus = data.status === "passed" ? "success" : data.status === "failed" ? "failed" : "pending";
|
|
136
184
|
const images = preservePreviousPassingImages(
|
|
137
185
|
test,
|
|
138
186
|
data.status,
|
|
139
|
-
attachmentsToImages(data.attachments, options.screenshotsBaseUrl)
|
|
187
|
+
mergeDeclaredImages(attachmentsToImages(data.attachments, options.screenshotsBaseUrl), data.visualNames)
|
|
140
188
|
);
|
|
141
189
|
const diffCount = countDiffImages(images);
|
|
142
190
|
const hasDiffs = diffCount > 0;
|
|
@@ -145,7 +193,7 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
145
193
|
}
|
|
146
194
|
test.results = [
|
|
147
195
|
{
|
|
148
|
-
status:
|
|
196
|
+
status: resultStatus,
|
|
149
197
|
retries: 0,
|
|
150
198
|
images,
|
|
151
199
|
error: data.error,
|
|
@@ -177,11 +225,13 @@ var LocationSchema = import_zod.z.object({
|
|
|
177
225
|
file: import_zod.z.string(),
|
|
178
226
|
line: import_zod.z.number()
|
|
179
227
|
});
|
|
228
|
+
var VisualSourceSchema = import_zod.z.enum(["comparison", "baseline-only", "declared-only"]);
|
|
180
229
|
var ImagesSchema = import_zod.z.object({
|
|
181
|
-
actual: import_zod.z.string(),
|
|
230
|
+
actual: import_zod.z.string().optional(),
|
|
182
231
|
expect: import_zod.z.string().optional(),
|
|
183
232
|
diff: import_zod.z.string().optional(),
|
|
184
|
-
error: import_zod.z.string().optional()
|
|
233
|
+
error: import_zod.z.string().optional(),
|
|
234
|
+
source: VisualSourceSchema.optional()
|
|
185
235
|
});
|
|
186
236
|
var AttachmentSchema = import_zod.z.object({
|
|
187
237
|
name: import_zod.z.string(),
|
|
@@ -189,10 +239,10 @@ var AttachmentSchema = import_zod.z.object({
|
|
|
189
239
|
contentType: import_zod.z.string()
|
|
190
240
|
});
|
|
191
241
|
var TestStatusSchema = import_zod.z.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
|
|
242
|
+
var TestResultStatusSchema = import_zod.z.enum(["failed", "success", "pending"]);
|
|
192
243
|
var TestResultSchema = import_zod.z.object({
|
|
193
|
-
status:
|
|
244
|
+
status: TestResultStatusSchema,
|
|
194
245
|
retries: import_zod.z.number(),
|
|
195
|
-
// eslint-disable-next-line typescript/no-unsafe-type-assertion
|
|
196
246
|
images: import_zod.z.record(import_zod.z.string(), ImagesSchema).optional(),
|
|
197
247
|
error: import_zod.z.string().optional(),
|
|
198
248
|
duration: import_zod.z.number().optional()
|
|
@@ -221,7 +271,6 @@ var CrvyRprtrSuiteSchema = import_zod.z.lazy(
|
|
|
221
271
|
opened: import_zod.z.boolean(),
|
|
222
272
|
checked: import_zod.z.boolean(),
|
|
223
273
|
indeterminate: import_zod.z.boolean(),
|
|
224
|
-
// eslint-disable-next-line typescript/no-unsafe-type-assertion
|
|
225
274
|
children: import_zod.z.record(import_zod.z.string(), import_zod.z.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
|
|
226
275
|
})
|
|
227
276
|
);
|
|
@@ -240,6 +289,7 @@ var TestEndDataSchema = import_zod.z.object({
|
|
|
240
289
|
id: import_zod.z.string(),
|
|
241
290
|
status: import_zod.z.enum(["passed", "failed", "skipped"]),
|
|
242
291
|
attachments: import_zod.z.array(AttachmentSchema),
|
|
292
|
+
visualNames: import_zod.z.array(import_zod.z.string()).default([]),
|
|
243
293
|
error: import_zod.z.string().optional(),
|
|
244
294
|
duration: import_zod.z.number().optional()
|
|
245
295
|
});
|
|
@@ -480,14 +530,122 @@ function handleSync(ctx) {
|
|
|
480
530
|
broadcastToBrowsers(ctx.wsClients, { type: "sync", data: ctx.reportData });
|
|
481
531
|
}
|
|
482
532
|
|
|
483
|
-
// src/server/
|
|
533
|
+
// src/server/report-watch.ts
|
|
534
|
+
var import_promises3 = require("fs/promises");
|
|
484
535
|
var import_path3 = require("path");
|
|
536
|
+
var OFFLINE_REPORT_FILE_PATTERN2 = /^crvy-rprtr(?:-\d+)?\.json$/;
|
|
537
|
+
function createDebouncedRefresh(reload, delayMs = 50) {
|
|
538
|
+
let timer = null;
|
|
539
|
+
return () => {
|
|
540
|
+
if (timer !== null) {
|
|
541
|
+
clearTimeout(timer);
|
|
542
|
+
}
|
|
543
|
+
timer = setTimeout(() => {
|
|
544
|
+
timer = null;
|
|
545
|
+
void reload();
|
|
546
|
+
}, delayMs);
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function isFileNotFound2(error) {
|
|
550
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
551
|
+
}
|
|
552
|
+
async function describeFile(filePath, label) {
|
|
553
|
+
try {
|
|
554
|
+
const fileStats = await (0, import_promises3.stat)(filePath);
|
|
555
|
+
return `${label}:${fileStats.size}:${fileStats.mtimeMs}`;
|
|
556
|
+
} catch (error) {
|
|
557
|
+
if (isFileNotFound2(error)) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async function createOfflineFingerprint(offlineReportDir) {
|
|
564
|
+
if (!await isDirectory(offlineReportDir)) {
|
|
565
|
+
return "offline:missing";
|
|
566
|
+
}
|
|
567
|
+
try {
|
|
568
|
+
const entries = await (0, import_promises3.readdir)(offlineReportDir, { withFileTypes: true });
|
|
569
|
+
const relevantEntries = entries.filter(
|
|
570
|
+
(entry) => entry.isFile() && (entry.name === "report.json" || OFFLINE_REPORT_FILE_PATTERN2.test(entry.name))
|
|
571
|
+
).sort((left, right) => left.name.localeCompare(right.name));
|
|
572
|
+
const parts = await Promise.all(
|
|
573
|
+
relevantEntries.map((entry) => describeFile((0, import_path3.join)(offlineReportDir, entry.name), entry.name))
|
|
574
|
+
);
|
|
575
|
+
return `offline:${parts.filter((part) => part !== null).join("|")}`;
|
|
576
|
+
} catch (error) {
|
|
577
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
578
|
+
console.warn(`[Server] Unable to scan ${offlineReportDir}: ${errorMsg}`);
|
|
579
|
+
return "offline:error";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async function createScreenshotFingerprint(screenshotDir, relativePath = "") {
|
|
583
|
+
const directoryPath = relativePath === "" ? screenshotDir : (0, import_path3.join)(screenshotDir, relativePath);
|
|
584
|
+
if (!await isDirectory(directoryPath)) {
|
|
585
|
+
return relativePath === "" ? ["screenshots:missing"] : [];
|
|
586
|
+
}
|
|
587
|
+
try {
|
|
588
|
+
const entries = await (0, import_promises3.readdir)(directoryPath, { withFileTypes: true });
|
|
589
|
+
const parts = await Promise.all(
|
|
590
|
+
entries.sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
591
|
+
const entryRelativePath = relativePath === "" ? entry.name : (0, import_path3.join)(relativePath, entry.name);
|
|
592
|
+
if (entry.isDirectory()) {
|
|
593
|
+
return createScreenshotFingerprint(screenshotDir, entryRelativePath);
|
|
594
|
+
}
|
|
595
|
+
return describeFile((0, import_path3.join)(screenshotDir, entryRelativePath), entryRelativePath);
|
|
596
|
+
})
|
|
597
|
+
);
|
|
598
|
+
return parts.flat().filter((part) => part !== null);
|
|
599
|
+
} catch (error) {
|
|
600
|
+
if (isFileNotFound2(error)) {
|
|
601
|
+
return relativePath === "" ? ["screenshots:missing"] : [];
|
|
602
|
+
}
|
|
603
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
604
|
+
console.warn(`[Server] Unable to scan ${directoryPath}: ${errorMsg}`);
|
|
605
|
+
return relativePath === "" ? ["screenshots:error"] : [];
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function createArtifactsFingerprint(options) {
|
|
609
|
+
const [offlineFingerprint, screenshotFingerprint] = await Promise.all([
|
|
610
|
+
createOfflineFingerprint(options.offlineReportDir),
|
|
611
|
+
createScreenshotFingerprint(options.screenshotDir)
|
|
612
|
+
]);
|
|
613
|
+
return `${offlineFingerprint}::${screenshotFingerprint.join("|")}`;
|
|
614
|
+
}
|
|
615
|
+
async function watchReportArtifacts(options) {
|
|
616
|
+
let fingerprint = await createArtifactsFingerprint(options);
|
|
617
|
+
let isPolling = false;
|
|
618
|
+
const interval = setInterval(() => {
|
|
619
|
+
if (isPolling) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
isPolling = true;
|
|
623
|
+
void createArtifactsFingerprint(options).then((nextFingerprint) => {
|
|
624
|
+
if (nextFingerprint === fingerprint) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
fingerprint = nextFingerprint;
|
|
628
|
+
options.scheduleRefresh();
|
|
629
|
+
}).catch((error) => {
|
|
630
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
631
|
+
console.warn(`[Server] Artifact refresh poll failed: ${errorMsg}`);
|
|
632
|
+
}).finally(() => {
|
|
633
|
+
isPolling = false;
|
|
634
|
+
});
|
|
635
|
+
}, 100);
|
|
636
|
+
return () => {
|
|
637
|
+
clearInterval(interval);
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/server/routes.ts
|
|
642
|
+
var import_path4 = require("path");
|
|
485
643
|
var LIVE_UPDATES_WEBSOCKET_PATH = "/";
|
|
486
644
|
function isWebSocketUpgradeRequest(req) {
|
|
487
645
|
return new URL(req.url).pathname === LIVE_UPDATES_WEBSOCKET_PATH && req.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
488
646
|
}
|
|
489
647
|
async function handleRoot(ctx) {
|
|
490
|
-
const html = await respondWithFile((0,
|
|
648
|
+
const html = await respondWithFile((0, import_path4.join)(ctx.staticDir, "index.html"), "text/html");
|
|
491
649
|
return html ?? new Response("Not Found", { status: 404 });
|
|
492
650
|
}
|
|
493
651
|
async function handleAppCss() {
|
|
@@ -584,7 +742,7 @@ async function handleScreenshots(ctx, req) {
|
|
|
584
742
|
}
|
|
585
743
|
async function handleDist(ctx, req) {
|
|
586
744
|
const path = new URL(req.url).pathname.slice("/dist/".length);
|
|
587
|
-
const filePath = (0,
|
|
745
|
+
const filePath = (0, import_path4.join)(ctx.staticDir, path);
|
|
588
746
|
const contentType = filePath.endsWith(".css") ? "text/css" : filePath.endsWith(".js") ? "application/javascript" : filePath.endsWith(".svelte") ? "text/plain" : "application/octet-stream";
|
|
589
747
|
const file = await respondWithFile(filePath, contentType);
|
|
590
748
|
return file ?? new Response("Not Found", { status: 404 });
|
|
@@ -688,6 +846,10 @@ async function loadOfflineReports(offlineReportDir, reportData) {
|
|
|
688
846
|
screenshotsBaseUrl: "/screenshots/"
|
|
689
847
|
});
|
|
690
848
|
}
|
|
849
|
+
function resetReloadableReportData(reportData) {
|
|
850
|
+
reportData.tests = {};
|
|
851
|
+
reportData.isUpdateMode = false;
|
|
852
|
+
}
|
|
691
853
|
async function handleParsedWebSocketMessage(ctx, msg) {
|
|
692
854
|
switch (msg.type) {
|
|
693
855
|
case "test-begin": {
|
|
@@ -736,19 +898,19 @@ function createWebSocketMessageHandler(getHandlerContext) {
|
|
|
736
898
|
};
|
|
737
899
|
}
|
|
738
900
|
async function resolveStaticDir(staticDir) {
|
|
739
|
-
const currentDir = (0,
|
|
901
|
+
const currentDir = (0, import_path5.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
740
902
|
const candidates = staticDir === void 0 ? [
|
|
741
903
|
currentDir,
|
|
742
|
-
(0,
|
|
743
|
-
(0,
|
|
744
|
-
(0,
|
|
745
|
-
(0,
|
|
746
|
-
(0,
|
|
747
|
-
] : [staticDir, (0,
|
|
904
|
+
(0, import_path5.join)(currentDir, "dist"),
|
|
905
|
+
(0, import_path5.join)(currentDir, "..", "dist"),
|
|
906
|
+
(0, import_path5.join)(currentDir, "..", "..", "dist"),
|
|
907
|
+
(0, import_path5.join)(currentDir, ".."),
|
|
908
|
+
(0, import_path5.join)(currentDir, "..", "..")
|
|
909
|
+
] : [staticDir, (0, import_path5.join)(staticDir, "dist")];
|
|
748
910
|
const resolvedCandidates = await Promise.all(
|
|
749
911
|
candidates.map(async (candidate) => ({
|
|
750
912
|
candidate,
|
|
751
|
-
exists: await fileExists((0,
|
|
913
|
+
exists: await fileExists((0, import_path5.join)(candidate, "index.html"))
|
|
752
914
|
}))
|
|
753
915
|
);
|
|
754
916
|
const resolved = resolvedCandidates.find(({ exists }) => exists);
|
|
@@ -759,9 +921,9 @@ async function resolveStaticDir(staticDir) {
|
|
|
759
921
|
}
|
|
760
922
|
async function resolveReportPath(reportPath) {
|
|
761
923
|
if (await isDirectory(reportPath)) {
|
|
762
|
-
return { reportFile: (0,
|
|
924
|
+
return { reportFile: (0, import_path5.join)(reportPath, "report.json"), offlineReportDir: reportPath };
|
|
763
925
|
}
|
|
764
|
-
return { reportFile: reportPath, offlineReportDir: (0,
|
|
926
|
+
return { reportFile: reportPath, offlineReportDir: (0, import_path5.dirname)(reportPath) };
|
|
765
927
|
}
|
|
766
928
|
async function createServerApp(options = {}) {
|
|
767
929
|
const port = options.port ?? 3e3;
|
|
@@ -782,11 +944,22 @@ async function createServerApp(options = {}) {
|
|
|
782
944
|
const getHandlerContext = () => createHandlerContext(reportData, wsClients, currentRunIds, saveReport);
|
|
783
945
|
const handleRequest = (req) => handleHttpRequest(routesContext, req);
|
|
784
946
|
const handleWebSocketMessage = createWebSocketMessageHandler(getHandlerContext);
|
|
785
|
-
|
|
786
|
-
|
|
947
|
+
const reloadFromDisk = async () => {
|
|
948
|
+
resetReloadableReportData(reportData);
|
|
949
|
+
await loadReport(reportFile, reportData);
|
|
950
|
+
await loadOfflineReports(offlineReportDir, reportData);
|
|
951
|
+
broadcastToBrowsers(wsClients, { type: "sync", data: reportData });
|
|
952
|
+
};
|
|
953
|
+
await reloadFromDisk();
|
|
954
|
+
const close = await watchReportArtifacts({
|
|
955
|
+
offlineReportDir,
|
|
956
|
+
screenshotDir: reportData.screenshotDir,
|
|
957
|
+
scheduleRefresh: createDebouncedRefresh(reloadFromDisk)
|
|
958
|
+
});
|
|
787
959
|
return {
|
|
788
960
|
port,
|
|
789
961
|
wsClients,
|
|
962
|
+
close,
|
|
790
963
|
handleRequest,
|
|
791
964
|
handleWebSocketMessage
|
|
792
965
|
};
|
package/dist/server.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export type VisualSource = 'comparison' | 'baseline-only' | 'declared-only';
|
|
1
2
|
export interface Images {
|
|
2
|
-
actual
|
|
3
|
+
actual?: string;
|
|
3
4
|
expect?: string;
|
|
4
5
|
diff?: string;
|
|
5
6
|
error?: string;
|
|
7
|
+
source?: VisualSource;
|
|
6
8
|
}
|
|
7
9
|
export interface Attachment {
|
|
8
10
|
name: string;
|
|
@@ -14,8 +16,9 @@ export interface Location {
|
|
|
14
16
|
line: number;
|
|
15
17
|
}
|
|
16
18
|
export type TestStatus = 'unknown' | 'pending' | 'running' | 'failed' | 'approved' | 'success' | 'retrying';
|
|
19
|
+
export type TestResultStatus = 'failed' | 'success' | 'pending';
|
|
17
20
|
export interface TestResult {
|
|
18
|
-
status:
|
|
21
|
+
status: TestResultStatus;
|
|
19
22
|
retries: number;
|
|
20
23
|
images?: Partial<Record<string, Images>>;
|
|
21
24
|
error?: string;
|
|
@@ -67,6 +70,7 @@ export interface TestEndMessage {
|
|
|
67
70
|
id: string;
|
|
68
71
|
status: 'passed' | 'failed' | 'skipped';
|
|
69
72
|
attachments: Attachment[];
|
|
73
|
+
visualNames: string[];
|
|
70
74
|
error?: string;
|
|
71
75
|
duration?: number;
|
|
72
76
|
};
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|