@crvy/rprtr 0.1.3 → 0.2.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 +84 -0
- package/README.md +15 -6
- package/dist/{chunk-X2TCDD54.js → chunk-HAFWYUNO.js} +204 -137
- package/dist/{chunk-F7NSIHCV.js → chunk-RFZGJSL3.js} +448 -128
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -4
- package/dist/index.css +29 -0
- package/dist/index.js +297 -76
- package/dist/offline-reports.d.ts +4 -0
- package/dist/offline-reports.d.ts.map +1 -1
- package/dist/path-utils.d.ts +3 -0
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/report-state.d.ts +3 -1
- package/dist/report-state.d.ts.map +1 -1
- package/dist/report-utils.d.ts.map +1 -1
- package/dist/reporter-utils.d.ts +0 -1
- package/dist/reporter-utils.d.ts.map +1 -1
- package/dist/reporter.cjs +232 -170
- package/dist/reporter.d.ts +1 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +8 -6
- package/dist/schemas/http.d.ts +44 -0
- package/dist/schemas/http.d.ts.map +1 -0
- package/dist/schemas.d.ts +25 -6
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server/app.d.ts +6 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/artifact-routes.d.ts.map +1 -1
- package/dist/server/handlers.d.ts +3 -0
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/playwright-config.d.ts +2 -0
- package/dist/server/playwright-config.d.ts.map +1 -0
- package/dist/server/routes-context.d.ts +13 -0
- package/dist/server/routes-context.d.ts.map +1 -0
- package/dist/server/routes.d.ts +6 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/run-controller.d.ts +85 -0
- package/dist/server/run-controller.d.ts.map +1 -0
- package/dist/server/run-routes.d.ts +3 -0
- package/dist/server/run-routes.d.ts.map +1 -0
- package/dist/server.cjs +668 -286
- package/dist/server.js +2 -2
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/reporter.cjs
CHANGED
|
@@ -36,7 +36,7 @@ __export(reporter_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(reporter_exports);
|
|
37
37
|
var import_fs = require("fs");
|
|
38
38
|
var import_promises3 = require("fs/promises");
|
|
39
|
-
var
|
|
39
|
+
var import_path4 = require("path");
|
|
40
40
|
var import_p_limit2 = __toESM(require("p-limit"), 1);
|
|
41
41
|
|
|
42
42
|
// src/ci.ts
|
|
@@ -56,16 +56,29 @@ var logError = (...args) => {
|
|
|
56
56
|
|
|
57
57
|
// src/reporter-artifact-ops.ts
|
|
58
58
|
var import_promises2 = require("fs/promises");
|
|
59
|
-
var
|
|
59
|
+
var import_path2 = require("path");
|
|
60
60
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
61
61
|
|
|
62
62
|
// src/report-artifact.ts
|
|
63
63
|
var import_promises = require("fs/promises");
|
|
64
|
-
var
|
|
64
|
+
var import_path = require("path");
|
|
65
65
|
var import_url = require("url");
|
|
66
66
|
|
|
67
|
+
// src/path-utils.ts
|
|
68
|
+
var posixAbsolutePattern = /^\//u;
|
|
69
|
+
var windowsDrivePattern = /^[A-Za-z]:[\\/]/u;
|
|
70
|
+
var windowsUncPattern = /^[\\/][\\/]/u;
|
|
71
|
+
function isPosixAbsolutePath(p) {
|
|
72
|
+
return posixAbsolutePattern.test(p);
|
|
73
|
+
}
|
|
74
|
+
function isWindowsAbsolutePath(p) {
|
|
75
|
+
return windowsDrivePattern.test(p) || windowsUncPattern.test(p);
|
|
76
|
+
}
|
|
77
|
+
function isAnyAbsolutePath(p) {
|
|
78
|
+
return isPosixAbsolutePath(p) || isWindowsAbsolutePath(p);
|
|
79
|
+
}
|
|
80
|
+
|
|
67
81
|
// src/report-utils.ts
|
|
68
|
-
var import_path = require("path");
|
|
69
82
|
function normalizeScreenshotsBaseUrl(baseUrl) {
|
|
70
83
|
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
71
84
|
}
|
|
@@ -110,7 +123,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
110
123
|
const role = match[2];
|
|
111
124
|
if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
|
|
112
125
|
images[baseName] ??= {};
|
|
113
|
-
const url = (
|
|
126
|
+
const url = isAnyAbsolutePath(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
|
|
114
127
|
const img = images[baseName];
|
|
115
128
|
if (img !== null && img !== void 0) {
|
|
116
129
|
if (role === "actual") img.actual = url;
|
|
@@ -145,9 +158,6 @@ function isCurrentArtifact(image) {
|
|
|
145
158
|
}
|
|
146
159
|
function isReusablePassingImage(image) {
|
|
147
160
|
const source = image.source ?? classifyImage(image);
|
|
148
|
-
if (source === "comparison") {
|
|
149
|
-
return image.actual !== void 0 && image.diff === void 0;
|
|
150
|
-
}
|
|
151
161
|
return source === "baseline-only";
|
|
152
162
|
}
|
|
153
163
|
function hasReusablePassingImages(images) {
|
|
@@ -168,8 +178,8 @@ function preservePreviousPassingImages(test, status, images) {
|
|
|
168
178
|
return {
|
|
169
179
|
...currentImages,
|
|
170
180
|
[name]: {
|
|
171
|
-
|
|
172
|
-
source:
|
|
181
|
+
expect: previousImage.expect,
|
|
182
|
+
source: "baseline-only"
|
|
173
183
|
}
|
|
174
184
|
};
|
|
175
185
|
}, images);
|
|
@@ -192,7 +202,12 @@ function createMutableReportState(screenshotDir = "./screenshots") {
|
|
|
192
202
|
function applyTestBeginEvent(state, data) {
|
|
193
203
|
const { id, title, titlePath, browser, projectName, location } = data;
|
|
194
204
|
state.currentRunIds.add(id);
|
|
195
|
-
state.reportData.tests[id]
|
|
205
|
+
const existing = state.reportData.tests[id];
|
|
206
|
+
if (existing !== void 0) {
|
|
207
|
+
existing.status = "running";
|
|
208
|
+
return existing;
|
|
209
|
+
}
|
|
210
|
+
const created = {
|
|
196
211
|
id,
|
|
197
212
|
titlePath: titlePath ?? [],
|
|
198
213
|
browser: browser ?? "",
|
|
@@ -204,7 +219,8 @@ function applyTestBeginEvent(state, data) {
|
|
|
204
219
|
location,
|
|
205
220
|
status: "running"
|
|
206
221
|
};
|
|
207
|
-
|
|
222
|
+
state.reportData.tests[id] = created;
|
|
223
|
+
return created;
|
|
208
224
|
}
|
|
209
225
|
function applyTestEndEvent(state, data, options = {}) {
|
|
210
226
|
const test = state.reportData.tests[data.id];
|
|
@@ -242,11 +258,13 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
242
258
|
diffCount
|
|
243
259
|
};
|
|
244
260
|
}
|
|
245
|
-
function finalizeRunEvent(state) {
|
|
261
|
+
function finalizeRunEvent(state, options = {}) {
|
|
246
262
|
state.reportData.isRunning = false;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
263
|
+
if (options.preserveNonCurrent !== true) {
|
|
264
|
+
state.reportData.tests = Object.fromEntries(
|
|
265
|
+
Object.entries(state.reportData.tests).filter(([id]) => state.currentRunIds.has(id))
|
|
266
|
+
);
|
|
267
|
+
}
|
|
250
268
|
state.currentRunIds.clear();
|
|
251
269
|
const runTests = Object.values(state.reportData.tests).filter((test) => test !== void 0);
|
|
252
270
|
return {
|
|
@@ -257,169 +275,211 @@ function finalizeRunEvent(state) {
|
|
|
257
275
|
}
|
|
258
276
|
|
|
259
277
|
// src/schemas.ts
|
|
278
|
+
var import_zod2 = require("zod");
|
|
279
|
+
|
|
280
|
+
// src/schemas/http.ts
|
|
260
281
|
var import_zod = require("zod");
|
|
261
|
-
var
|
|
282
|
+
var ApproveRequestBodySchema = import_zod.z.object({
|
|
283
|
+
id: import_zod.z.string(),
|
|
284
|
+
retry: import_zod.z.number(),
|
|
285
|
+
image: import_zod.z.string()
|
|
286
|
+
});
|
|
287
|
+
var RunTestDescriptorSchema = import_zod.z.object({
|
|
262
288
|
file: import_zod.z.string(),
|
|
263
|
-
line: import_zod.z.number()
|
|
289
|
+
line: import_zod.z.number(),
|
|
290
|
+
column: import_zod.z.number().optional(),
|
|
291
|
+
projectName: import_zod.z.string().optional(),
|
|
292
|
+
titlePath: import_zod.z.array(import_zod.z.string())
|
|
264
293
|
});
|
|
265
|
-
var
|
|
266
|
-
|
|
267
|
-
actual: import_zod.z.string().optional(),
|
|
268
|
-
expect: import_zod.z.string().optional(),
|
|
269
|
-
diff: import_zod.z.string().optional(),
|
|
270
|
-
error: import_zod.z.string().optional(),
|
|
271
|
-
source: VisualSourceSchema.optional()
|
|
294
|
+
var RunRequestBodySchema = import_zod.z.object({
|
|
295
|
+
tests: import_zod.z.array(RunTestDescriptorSchema).optional()
|
|
272
296
|
});
|
|
273
|
-
var
|
|
297
|
+
var RunResponseSchema = import_zod.z.discriminatedUnion("ok", [
|
|
298
|
+
import_zod.z.object({ ok: import_zod.z.literal(true) }),
|
|
274
299
|
import_zod.z.object({
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}),
|
|
300
|
+
ok: import_zod.z.literal(false),
|
|
301
|
+
reason: import_zod.z.enum(["no-config", "already-running", "no-tests"])
|
|
302
|
+
})
|
|
303
|
+
]);
|
|
304
|
+
var StopResponseSchema = import_zod.z.discriminatedUnion("ok", [
|
|
305
|
+
import_zod.z.object({ ok: import_zod.z.literal(true) }),
|
|
281
306
|
import_zod.z.object({
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
occurrenceIndex: import_zod.z.number()
|
|
307
|
+
ok: import_zod.z.literal(false),
|
|
308
|
+
reason: import_zod.z.literal("not-running")
|
|
285
309
|
})
|
|
286
310
|
]);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
311
|
+
|
|
312
|
+
// src/schemas.ts
|
|
313
|
+
var LocationSchema = import_zod2.z.object({
|
|
314
|
+
file: import_zod2.z.string(),
|
|
315
|
+
line: import_zod2.z.number(),
|
|
316
|
+
column: import_zod2.z.number().optional()
|
|
291
317
|
});
|
|
292
|
-
var
|
|
293
|
-
var
|
|
294
|
-
|
|
318
|
+
var VisualSourceSchema = import_zod2.z.enum(["comparison", "baseline-only", "declared-only"]);
|
|
319
|
+
var ImagesSchema = import_zod2.z.object({
|
|
320
|
+
actual: import_zod2.z.string().optional(),
|
|
321
|
+
expect: import_zod2.z.string().optional(),
|
|
322
|
+
diff: import_zod2.z.string().optional(),
|
|
323
|
+
error: import_zod2.z.string().optional(),
|
|
324
|
+
source: VisualSourceSchema.optional()
|
|
325
|
+
});
|
|
326
|
+
var ScreenshotDeclarationSchema = import_zod2.z.discriminatedUnion("kind", [
|
|
327
|
+
import_zod2.z.object({
|
|
328
|
+
visualName: import_zod2.z.string(),
|
|
329
|
+
kind: import_zod2.z.literal("named"),
|
|
330
|
+
declaredName: import_zod2.z.string(),
|
|
331
|
+
snapshotBaseName: import_zod2.z.string(),
|
|
332
|
+
occurrenceIndex: import_zod2.z.number()
|
|
333
|
+
}),
|
|
334
|
+
import_zod2.z.object({
|
|
335
|
+
visualName: import_zod2.z.string(),
|
|
336
|
+
kind: import_zod2.z.literal("unnamed"),
|
|
337
|
+
occurrenceIndex: import_zod2.z.number()
|
|
338
|
+
})
|
|
339
|
+
]);
|
|
340
|
+
var AttachmentSchema = import_zod2.z.object({
|
|
341
|
+
name: import_zod2.z.string(),
|
|
342
|
+
path: import_zod2.z.string(),
|
|
343
|
+
contentType: import_zod2.z.string()
|
|
344
|
+
});
|
|
345
|
+
var TestStatusSchema = import_zod2.z.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
|
|
346
|
+
var TestResultStatusSchema = import_zod2.z.enum(["failed", "success", "pending"]);
|
|
347
|
+
var TestResultSchema = import_zod2.z.object({
|
|
295
348
|
status: TestResultStatusSchema,
|
|
296
|
-
retries:
|
|
297
|
-
images:
|
|
298
|
-
visualDeclarations:
|
|
299
|
-
error:
|
|
300
|
-
duration:
|
|
349
|
+
retries: import_zod2.z.number(),
|
|
350
|
+
images: import_zod2.z.record(import_zod2.z.string(), ImagesSchema).optional(),
|
|
351
|
+
visualDeclarations: import_zod2.z.array(ScreenshotDeclarationSchema).optional(),
|
|
352
|
+
error: import_zod2.z.string().optional(),
|
|
353
|
+
duration: import_zod2.z.number().optional()
|
|
301
354
|
});
|
|
302
|
-
var TestDataSchema =
|
|
303
|
-
id:
|
|
304
|
-
titlePath:
|
|
305
|
-
browser:
|
|
306
|
-
projectName:
|
|
307
|
-
title:
|
|
308
|
-
skip:
|
|
309
|
-
retries:
|
|
355
|
+
var TestDataSchema = import_zod2.z.object({
|
|
356
|
+
id: import_zod2.z.string(),
|
|
357
|
+
titlePath: import_zod2.z.array(import_zod2.z.string()),
|
|
358
|
+
browser: import_zod2.z.string(),
|
|
359
|
+
projectName: import_zod2.z.string().optional(),
|
|
360
|
+
title: import_zod2.z.string(),
|
|
361
|
+
skip: import_zod2.z.union([import_zod2.z.boolean(), import_zod2.z.string()]).optional(),
|
|
362
|
+
retries: import_zod2.z.number().optional(),
|
|
310
363
|
status: TestStatusSchema.optional(),
|
|
311
|
-
results:
|
|
312
|
-
approved:
|
|
313
|
-
attachments:
|
|
364
|
+
results: import_zod2.z.array(TestResultSchema).optional(),
|
|
365
|
+
approved: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.number()).nullable().optional(),
|
|
366
|
+
attachments: import_zod2.z.array(AttachmentSchema).optional(),
|
|
314
367
|
location: LocationSchema.optional()
|
|
315
368
|
});
|
|
316
369
|
var CrvyRprtrTestSchema = TestDataSchema.extend({
|
|
317
|
-
checked:
|
|
370
|
+
checked: import_zod2.z.boolean()
|
|
318
371
|
});
|
|
319
|
-
var CrvyRprtrSuiteSchema =
|
|
320
|
-
() =>
|
|
321
|
-
path:
|
|
322
|
-
skip:
|
|
372
|
+
var CrvyRprtrSuiteSchema = import_zod2.z.lazy(
|
|
373
|
+
() => import_zod2.z.object({
|
|
374
|
+
path: import_zod2.z.array(import_zod2.z.string()),
|
|
375
|
+
skip: import_zod2.z.boolean(),
|
|
323
376
|
status: TestStatusSchema.optional(),
|
|
324
|
-
opened:
|
|
325
|
-
checked:
|
|
326
|
-
indeterminate:
|
|
327
|
-
children:
|
|
377
|
+
opened: import_zod2.z.boolean(),
|
|
378
|
+
checked: import_zod2.z.boolean(),
|
|
379
|
+
indeterminate: import_zod2.z.boolean(),
|
|
380
|
+
children: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
|
|
328
381
|
})
|
|
329
382
|
);
|
|
330
|
-
var IncomingWebSocketMessageSchema =
|
|
331
|
-
type:
|
|
332
|
-
data:
|
|
383
|
+
var IncomingWebSocketMessageSchema = import_zod2.z.object({
|
|
384
|
+
type: import_zod2.z.enum(["test-begin", "test-end", "run-end", "approve", "sync", "register"]),
|
|
385
|
+
data: import_zod2.z.unknown()
|
|
333
386
|
});
|
|
334
|
-
var WebSocketMessageSchema =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
type:
|
|
339
|
-
data:
|
|
340
|
-
status:
|
|
341
|
-
removedTestIds:
|
|
387
|
+
var WebSocketMessageSchema = import_zod2.z.discriminatedUnion("type", [
|
|
388
|
+
import_zod2.z.object({ type: import_zod2.z.literal("test-begin"), data: TestDataSchema }),
|
|
389
|
+
import_zod2.z.object({ type: import_zod2.z.literal("test-update"), data: TestDataSchema }),
|
|
390
|
+
import_zod2.z.object({
|
|
391
|
+
type: import_zod2.z.literal("run-end"),
|
|
392
|
+
data: import_zod2.z.object({
|
|
393
|
+
status: import_zod2.z.enum(["passed", "failed", "skipped"]),
|
|
394
|
+
removedTestIds: import_zod2.z.array(import_zod2.z.string())
|
|
342
395
|
})
|
|
343
396
|
}),
|
|
344
|
-
|
|
345
|
-
type:
|
|
346
|
-
data:
|
|
347
|
-
tests:
|
|
348
|
-
isUpdateMode:
|
|
397
|
+
import_zod2.z.object({
|
|
398
|
+
type: import_zod2.z.literal("sync"),
|
|
399
|
+
data: import_zod2.z.object({
|
|
400
|
+
tests: import_zod2.z.record(import_zod2.z.string(), TestDataSchema),
|
|
401
|
+
isUpdateMode: import_zod2.z.boolean().optional()
|
|
349
402
|
})
|
|
350
403
|
}),
|
|
351
|
-
|
|
404
|
+
import_zod2.z.object({ type: import_zod2.z.literal("approve"), data: import_zod2.z.unknown() }),
|
|
405
|
+
import_zod2.z.object({
|
|
406
|
+
type: import_zod2.z.literal("run-status"),
|
|
407
|
+
data: import_zod2.z.object({
|
|
408
|
+
running: import_zod2.z.boolean()
|
|
409
|
+
})
|
|
410
|
+
})
|
|
352
411
|
]);
|
|
353
|
-
var TestBeginDataSchema =
|
|
354
|
-
id:
|
|
355
|
-
title:
|
|
356
|
-
titlePath:
|
|
357
|
-
browser:
|
|
358
|
-
projectName:
|
|
412
|
+
var TestBeginDataSchema = import_zod2.z.object({
|
|
413
|
+
id: import_zod2.z.string(),
|
|
414
|
+
title: import_zod2.z.string(),
|
|
415
|
+
titlePath: import_zod2.z.array(import_zod2.z.string()),
|
|
416
|
+
browser: import_zod2.z.string(),
|
|
417
|
+
projectName: import_zod2.z.string().optional(),
|
|
359
418
|
location: LocationSchema
|
|
360
419
|
});
|
|
361
|
-
var TestEndDataSchema =
|
|
362
|
-
id:
|
|
363
|
-
status:
|
|
364
|
-
attachments:
|
|
365
|
-
visualNames:
|
|
366
|
-
visualDeclarations:
|
|
420
|
+
var TestEndDataSchema = import_zod2.z.object({
|
|
421
|
+
id: import_zod2.z.string(),
|
|
422
|
+
status: import_zod2.z.enum(["passed", "failed", "skipped"]),
|
|
423
|
+
attachments: import_zod2.z.array(AttachmentSchema),
|
|
424
|
+
visualNames: import_zod2.z.array(import_zod2.z.string()).default([]),
|
|
425
|
+
visualDeclarations: import_zod2.z.preprocess(
|
|
367
426
|
(value) => value === null ? void 0 : value,
|
|
368
|
-
|
|
427
|
+
import_zod2.z.array(ScreenshotDeclarationSchema).optional()
|
|
369
428
|
),
|
|
370
|
-
error:
|
|
371
|
-
duration:
|
|
372
|
-
});
|
|
373
|
-
var RunEndDataSchema = import_zod.z.object({
|
|
374
|
-
status: import_zod.z.enum(["passed", "failed", "skipped"])
|
|
429
|
+
error: import_zod2.z.string().optional(),
|
|
430
|
+
duration: import_zod2.z.number().optional()
|
|
375
431
|
});
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
playwrightTestDir: import_zod.z.string().optional(),
|
|
379
|
-
playwrightSnapshotPathTemplate: import_zod.z.string().optional(),
|
|
380
|
-
playwrightToHaveScreenshotPathTemplate: import_zod.z.string().optional()
|
|
432
|
+
var RunEndDataSchema = import_zod2.z.object({
|
|
433
|
+
status: import_zod2.z.enum(["passed", "failed", "skipped"])
|
|
381
434
|
});
|
|
382
|
-
var
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
435
|
+
var RegisterDataSchema = import_zod2.z.object({
|
|
436
|
+
playwrightSnapshotDir: import_zod2.z.string().optional(),
|
|
437
|
+
playwrightTestDir: import_zod2.z.string().optional(),
|
|
438
|
+
playwrightSnapshotPathTemplate: import_zod2.z.string().optional(),
|
|
439
|
+
playwrightToHaveScreenshotPathTemplate: import_zod2.z.string().optional(),
|
|
440
|
+
configFile: import_zod2.z.string().optional(),
|
|
441
|
+
cwd: import_zod2.z.string().optional()
|
|
388
442
|
});
|
|
389
|
-
var
|
|
390
|
-
|
|
391
|
-
|
|
443
|
+
var ReportDataSchema = import_zod2.z.object({
|
|
444
|
+
isRunning: import_zod2.z.boolean(),
|
|
445
|
+
tests: import_zod2.z.record(import_zod2.z.string(), TestDataSchema),
|
|
446
|
+
browsers: import_zod2.z.array(import_zod2.z.string()),
|
|
447
|
+
isUpdateMode: import_zod2.z.boolean(),
|
|
448
|
+
screenshotDir: import_zod2.z.string()
|
|
392
449
|
});
|
|
393
|
-
var
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
timestamp: import_zod.z.number(),
|
|
397
|
-
workerIndex: import_zod.z.number()
|
|
450
|
+
var LoadedReportDataSchema = import_zod2.z.object({
|
|
451
|
+
tests: import_zod2.z.record(import_zod2.z.string(), TestDataSchema).optional(),
|
|
452
|
+
isUpdateMode: import_zod2.z.boolean().optional()
|
|
398
453
|
});
|
|
399
|
-
var
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
454
|
+
var OfflineEventSchema = import_zod2.z.object({
|
|
455
|
+
type: import_zod2.z.enum(["test-begin", "test-end", "run-end"]),
|
|
456
|
+
data: import_zod2.z.unknown(),
|
|
457
|
+
timestamp: import_zod2.z.number(),
|
|
458
|
+
workerIndex: import_zod2.z.number()
|
|
404
459
|
});
|
|
405
|
-
var
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
460
|
+
var OfflineReportSchema = import_zod2.z.object({
|
|
461
|
+
version: import_zod2.z.number(),
|
|
462
|
+
generatedAt: import_zod2.z.string(),
|
|
463
|
+
workers: import_zod2.z.number(),
|
|
464
|
+
events: import_zod2.z.array(OfflineEventSchema)
|
|
409
465
|
});
|
|
410
|
-
var ReportApiResponseSchema =
|
|
411
|
-
tests:
|
|
412
|
-
isUpdateMode:
|
|
466
|
+
var ReportApiResponseSchema = import_zod2.z.object({
|
|
467
|
+
tests: import_zod2.z.record(import_zod2.z.string(), TestDataSchema),
|
|
468
|
+
isUpdateMode: import_zod2.z.boolean().optional(),
|
|
469
|
+
isRunning: import_zod2.z.boolean().optional(),
|
|
470
|
+
runEnabled: import_zod2.z.boolean().optional()
|
|
413
471
|
});
|
|
414
|
-
var ClientBootstrapDataSchema =
|
|
472
|
+
var ClientBootstrapDataSchema = import_zod2.z.object({
|
|
415
473
|
report: ReportApiResponseSchema.extend({
|
|
416
|
-
isUpdateMode:
|
|
474
|
+
isUpdateMode: import_zod2.z.boolean()
|
|
417
475
|
}),
|
|
418
|
-
liveUpdates:
|
|
419
|
-
approvalEnabled:
|
|
420
|
-
approvalMessage:
|
|
476
|
+
liveUpdates: import_zod2.z.boolean(),
|
|
477
|
+
approvalEnabled: import_zod2.z.boolean(),
|
|
478
|
+
approvalMessage: import_zod2.z.string().optional(),
|
|
479
|
+
runEnabled: import_zod2.z.boolean().optional(),
|
|
480
|
+
isRunning: import_zod2.z.boolean().optional()
|
|
421
481
|
});
|
|
422
|
-
var ImagesViewModeSchema =
|
|
482
|
+
var ImagesViewModeSchema = import_zod2.z.enum(["side-by-side", "swap", "slide", "blend"]);
|
|
423
483
|
function safeParse(schema, data) {
|
|
424
484
|
const result = schema.safeParse(data);
|
|
425
485
|
if (result.success) {
|
|
@@ -433,7 +493,7 @@ var import_meta = { url: require("url").pathToFileURL(__filename).href };
|
|
|
433
493
|
var DEFAULT_REPORT_HTML_PATH = "./crvy-rprtr.html";
|
|
434
494
|
var STATIC_ARTIFACT_APPROVAL_MESSAGE = "This artifact is read-only. Open it with the Crvy Rprtr server to approve screenshots.";
|
|
435
495
|
function toBrowserPath(pathValue) {
|
|
436
|
-
const normalized = pathValue.split(
|
|
496
|
+
const normalized = pathValue.split(import_path.sep).join("/");
|
|
437
497
|
if (normalized === "") return ".";
|
|
438
498
|
if (normalized.startsWith(".")) return normalized;
|
|
439
499
|
return `./${normalized}`;
|
|
@@ -469,11 +529,11 @@ function renderArtifactHtml(template, bootstrapData, stylesheet, script) {
|
|
|
469
529
|
);
|
|
470
530
|
}
|
|
471
531
|
async function getPackagedAssetPath(fileName) {
|
|
472
|
-
const currentDir = (0,
|
|
473
|
-
const candidates = [currentDir, (0,
|
|
532
|
+
const currentDir = (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
533
|
+
const candidates = [currentDir, (0, import_path.resolve)(currentDir, "../dist")];
|
|
474
534
|
const resolvedCandidates = await Promise.all(
|
|
475
535
|
candidates.map(async (candidate) => {
|
|
476
|
-
const assetPath = (0,
|
|
536
|
+
const assetPath = (0, import_path.resolve)(candidate, fileName);
|
|
477
537
|
try {
|
|
478
538
|
await (0, import_promises.access)(assetPath);
|
|
479
539
|
return assetPath;
|
|
@@ -491,7 +551,7 @@ async function getPackagedAssetPath(fileName) {
|
|
|
491
551
|
);
|
|
492
552
|
}
|
|
493
553
|
function buildStaticBootstrapData(events, screenshotDir, htmlPath) {
|
|
494
|
-
const screenshotBaseUrl = toBrowserDirPath((0,
|
|
554
|
+
const screenshotBaseUrl = toBrowserDirPath((0, import_path.relative)((0, import_path.dirname)(htmlPath), (0, import_path.resolve)(screenshotDir)));
|
|
495
555
|
const state = createMutableReportState(screenshotDir);
|
|
496
556
|
for (const event of events) {
|
|
497
557
|
switch (event.type) {
|
|
@@ -530,13 +590,13 @@ function buildStaticBootstrapData(events, screenshotDir, htmlPath) {
|
|
|
530
590
|
return parsedBootstrapData;
|
|
531
591
|
}
|
|
532
592
|
async function writeReportArtifact(options) {
|
|
533
|
-
const reportHtmlPath = (0,
|
|
593
|
+
const reportHtmlPath = (0, import_path.resolve)(options.reportHtmlPath ?? DEFAULT_REPORT_HTML_PATH);
|
|
534
594
|
const [indexHtmlPath, indexJsPath, indexCssPath] = await Promise.all([
|
|
535
595
|
getPackagedAssetPath("index.html"),
|
|
536
596
|
getPackagedAssetPath("index.js"),
|
|
537
597
|
getPackagedAssetPath("index.css")
|
|
538
598
|
]);
|
|
539
|
-
await (0, import_promises.mkdir)((0,
|
|
599
|
+
await (0, import_promises.mkdir)((0, import_path.dirname)(reportHtmlPath), { recursive: true });
|
|
540
600
|
const [template, script, stylesheet] = await Promise.all([
|
|
541
601
|
(0, import_promises.readFile)(indexHtmlPath, "utf8"),
|
|
542
602
|
(0, import_promises.readFile)(indexJsPath, "utf8"),
|
|
@@ -569,9 +629,9 @@ function sanitizeId(id) {
|
|
|
569
629
|
async function copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments) {
|
|
570
630
|
const attachmentName = `${target.attachmentBaseName}-expected.png`;
|
|
571
631
|
const artifactPath = safeArtifactPath(attachmentName);
|
|
572
|
-
const destPath = (0,
|
|
632
|
+
const destPath = (0, import_path2.join)(testScreenshotDir, artifactPath);
|
|
573
633
|
try {
|
|
574
|
-
await (0, import_promises2.mkdir)((0,
|
|
634
|
+
await (0, import_promises2.mkdir)((0, import_path2.dirname)(destPath), { recursive: true });
|
|
575
635
|
await (0, import_promises2.copyFile)(target.snapshotPath, destPath);
|
|
576
636
|
savedAttachments.push({ name: attachmentName, path: `${safeTestId}/${artifactPath}`, contentType: "image/png" });
|
|
577
637
|
log(`[CrvyRprtr] Attached baseline: ${target.snapshotPath}`);
|
|
@@ -581,7 +641,7 @@ async function copyResolvedBaseline(safeTestId, testScreenshotDir, target, saved
|
|
|
581
641
|
async function saveAttachments(screenshotDir, testId, result) {
|
|
582
642
|
const savedAttachments = [];
|
|
583
643
|
const safeTestId = sanitizeId(testId);
|
|
584
|
-
const testScreenshotDir = (0,
|
|
644
|
+
const testScreenshotDir = (0, import_path2.join)(screenshotDir, safeTestId);
|
|
585
645
|
const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
|
|
586
646
|
await Promise.all(
|
|
587
647
|
result.attachments.filter(
|
|
@@ -590,8 +650,8 @@ async function saveAttachments(screenshotDir, testId, result) {
|
|
|
590
650
|
(attachment) => limit(async () => {
|
|
591
651
|
try {
|
|
592
652
|
const artifactPath = safeArtifactPath(attachment.name);
|
|
593
|
-
const destPath = (0,
|
|
594
|
-
await (0, import_promises2.mkdir)((0,
|
|
653
|
+
const destPath = (0, import_path2.join)(testScreenshotDir, artifactPath);
|
|
654
|
+
await (0, import_promises2.mkdir)((0, import_path2.dirname)(destPath), { recursive: true });
|
|
595
655
|
await (0, import_promises2.copyFile)(attachment.path, destPath);
|
|
596
656
|
savedAttachments.push({
|
|
597
657
|
name: attachment.name,
|
|
@@ -721,7 +781,7 @@ function extractScreenshotDeclarations(steps) {
|
|
|
721
781
|
|
|
722
782
|
// src/snapshot-path-resolver.ts
|
|
723
783
|
var import_crypto = require("crypto");
|
|
724
|
-
var
|
|
784
|
+
var import_path3 = require("path");
|
|
725
785
|
var DEFAULT_SCREENSHOT_TEMPLATE = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
|
|
726
786
|
var WINDOWS_FILESYSTEM_FRIENDLY_LENGTH = 60;
|
|
727
787
|
function isUnsafeFilePathCharacter(character) {
|
|
@@ -756,16 +816,16 @@ function trimLongString(value, length = WINDOWS_FILESYSTEM_FRIENDLY_LENGTH) {
|
|
|
756
816
|
const end = length - middle.length - start;
|
|
757
817
|
return value.slice(0, start) + middle + value.slice(-end);
|
|
758
818
|
}
|
|
759
|
-
function sanitizeFilePathBeforeExtension(filePath, extension = (0,
|
|
819
|
+
function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
|
|
760
820
|
const base = filePath.slice(0, filePath.length - extension.length);
|
|
761
821
|
return sanitizeForFilePath(base) + extension;
|
|
762
822
|
}
|
|
763
823
|
function addSuffixToFilePath(filePath, suffix) {
|
|
764
|
-
const extension = (0,
|
|
824
|
+
const extension = (0, import_path3.extname)(filePath);
|
|
765
825
|
return filePath.slice(0, filePath.length - extension.length) + suffix + extension;
|
|
766
826
|
}
|
|
767
827
|
function normalizedSnapshotDir(config) {
|
|
768
|
-
return (0,
|
|
828
|
+
return (0, import_path3.resolve)(config.configDir, config.snapshotDir);
|
|
769
829
|
}
|
|
770
830
|
function templateValue(template, token, value) {
|
|
771
831
|
return template.replace(
|
|
@@ -775,8 +835,8 @@ function templateValue(template, token, value) {
|
|
|
775
835
|
}
|
|
776
836
|
function applyTemplate(input, nameArgument, extension) {
|
|
777
837
|
const template = input.config.toHaveScreenshotPathTemplate ?? input.config.snapshotPathTemplate ?? DEFAULT_SCREENSHOT_TEMPLATE;
|
|
778
|
-
const relativeTestFilePath = (0,
|
|
779
|
-
const parsed = (0,
|
|
838
|
+
const relativeTestFilePath = (0, import_path3.relative)(input.config.testDir, input.testFile);
|
|
839
|
+
const parsed = (0, import_path3.parse)(relativeTestFilePath);
|
|
780
840
|
const tokens = [
|
|
781
841
|
["testDir", input.config.testDir],
|
|
782
842
|
["snapshotDir", normalizedSnapshotDir(input.config)],
|
|
@@ -794,16 +854,16 @@ function applyTemplate(input, nameArgument, extension) {
|
|
|
794
854
|
(currentTemplate, [token, value]) => templateValue(currentTemplate, token, value),
|
|
795
855
|
template
|
|
796
856
|
);
|
|
797
|
-
return (0,
|
|
857
|
+
return (0, import_path3.resolve)(input.config.configDir, snapshotPath);
|
|
798
858
|
}
|
|
799
|
-
function removeExtension(filePath, extension = (0,
|
|
859
|
+
function removeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
|
|
800
860
|
return filePath.slice(0, filePath.length - extension.length);
|
|
801
861
|
}
|
|
802
862
|
function snapshotNameParts(declaredName) {
|
|
803
|
-
const extension = (0,
|
|
863
|
+
const extension = (0, import_path3.extname)(declaredName) || ".png";
|
|
804
864
|
return {
|
|
805
865
|
extension,
|
|
806
|
-
filePath: (0,
|
|
866
|
+
filePath: (0, import_path3.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
|
|
807
867
|
};
|
|
808
868
|
}
|
|
809
869
|
function filePathForOccurrence(filePath, occurrenceIndex) {
|
|
@@ -827,7 +887,7 @@ function resolveStringCallTarget(input, declaration) {
|
|
|
827
887
|
function resolveArrayCallTarget(input, declaration) {
|
|
828
888
|
const { extension, filePath } = snapshotNameParts(declaration.declaredName);
|
|
829
889
|
const occurrenceFilePath = filePathForOccurrence(filePath, declaration.occurrenceIndex);
|
|
830
|
-
const nameArgument = (0,
|
|
890
|
+
const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(occurrenceFilePath), (0, import_path3.basename)(occurrenceFilePath, extension));
|
|
831
891
|
return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
|
|
832
892
|
}
|
|
833
893
|
function resolveNamedTarget(input, declaration) {
|
|
@@ -869,7 +929,7 @@ function resolveTarget(input, declaration) {
|
|
|
869
929
|
case "unnamed": {
|
|
870
930
|
const anonymousFileName = anonymousName(input.reporterTitlePath, declaration.occurrenceIndex);
|
|
871
931
|
const extension = ".png";
|
|
872
|
-
const nameArgument = (0,
|
|
932
|
+
const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(anonymousFileName), (0, import_path3.basename)(anonymousFileName, extension));
|
|
873
933
|
return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
|
|
874
934
|
}
|
|
875
935
|
}
|
|
@@ -909,7 +969,7 @@ var CrvyRprtr = class {
|
|
|
909
969
|
ci;
|
|
910
970
|
pendingArtifacts = [];
|
|
911
971
|
constructor(options = {}) {
|
|
912
|
-
this.serverUrl = options.serverUrl ?? "ws://localhost:3000";
|
|
972
|
+
this.serverUrl = options.serverUrl ?? process.env.CRVY_RPRTR_SERVER_URL ?? "ws://localhost:3000";
|
|
913
973
|
this.screenshotDir = options.screenshotDir ?? "./screenshots";
|
|
914
974
|
this.workerIndex = parseInt(process.env.TEST_WORKER_INDEX ?? "0", 10) || 0;
|
|
915
975
|
this.offlineReportPath = options.offlineReportPath ?? `./crvy-rprtr-${this.workerIndex}.json`;
|
|
@@ -920,10 +980,9 @@ var CrvyRprtr = class {
|
|
|
920
980
|
this.ci = options.ci ?? isCI();
|
|
921
981
|
if (this.ci) this.isOfflineMode = true;
|
|
922
982
|
}
|
|
923
|
-
|
|
924
|
-
this.configDir = config.configFile === void 0 ? config.rootDir : (0,
|
|
983
|
+
onBegin(config, suite) {
|
|
984
|
+
this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path4.dirname)(config.configFile);
|
|
925
985
|
log(`[CrvyRprtr] Starting run with ${suite.allTests().length} tests`);
|
|
926
|
-
await (0, import_promises3.mkdir)(this.screenshotDir, { recursive: true });
|
|
927
986
|
if (!this.ci) {
|
|
928
987
|
this.connect();
|
|
929
988
|
this.sendRegister(config);
|
|
@@ -971,7 +1030,9 @@ var CrvyRprtr = class {
|
|
|
971
1030
|
playwrightSnapshotDir: typeof snapshotDir === "string" ? snapshotDir : void 0,
|
|
972
1031
|
playwrightTestDir: typeof testDir === "string" ? testDir : void 0,
|
|
973
1032
|
playwrightSnapshotPathTemplate: this.playwrightSnapshotPathTemplate,
|
|
974
|
-
playwrightToHaveScreenshotPathTemplate: this.playwrightToHaveScreenshotPathTemplate
|
|
1033
|
+
playwrightToHaveScreenshotPathTemplate: this.playwrightToHaveScreenshotPathTemplate,
|
|
1034
|
+
configFile: config.configFile,
|
|
1035
|
+
cwd: config.rootDir ?? process.cwd()
|
|
975
1036
|
}
|
|
976
1037
|
});
|
|
977
1038
|
}
|
|
@@ -1009,7 +1070,7 @@ var CrvyRprtr = class {
|
|
|
1009
1070
|
titlePath: this.describeTitlePath(test),
|
|
1010
1071
|
browser: this.resolveBrowserLabel(project),
|
|
1011
1072
|
projectName: project?.name ?? "",
|
|
1012
|
-
location: { file: test.location.file, line: test.location.line }
|
|
1073
|
+
location: { file: test.location.file, line: test.location.line, column: test.location.column }
|
|
1013
1074
|
}
|
|
1014
1075
|
});
|
|
1015
1076
|
}
|
|
@@ -1070,7 +1131,7 @@ var CrvyRprtr = class {
|
|
|
1070
1131
|
async copyBaselinesForTargets(testId, status, resolvedTargets, savedAttachments) {
|
|
1071
1132
|
if (status !== "passed" || resolvedTargets.length === 0) return;
|
|
1072
1133
|
const safeTestId = sanitizeId(testId);
|
|
1073
|
-
const testScreenshotDir = (0,
|
|
1134
|
+
const testScreenshotDir = (0, import_path4.join)(this.screenshotDir, safeTestId);
|
|
1074
1135
|
const limit = (0, import_p_limit2.default)(5);
|
|
1075
1136
|
await Promise.all(
|
|
1076
1137
|
resolvedTargets.map(
|
|
@@ -1081,6 +1142,7 @@ var CrvyRprtr = class {
|
|
|
1081
1142
|
async onEnd(result) {
|
|
1082
1143
|
this.send({ type: "run-end", data: { status: result.status } });
|
|
1083
1144
|
if (this.ci) {
|
|
1145
|
+
await (0, import_promises3.mkdir)(this.screenshotDir, { recursive: true });
|
|
1084
1146
|
const limit = (0, import_p_limit2.default)(10);
|
|
1085
1147
|
await Promise.all(
|
|
1086
1148
|
this.pendingArtifacts.map(
|