@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +15 -6
  3. package/dist/{chunk-X2TCDD54.js → chunk-HAFWYUNO.js} +204 -137
  4. package/dist/{chunk-F7NSIHCV.js → chunk-RFZGJSL3.js} +448 -128
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +6 -4
  7. package/dist/index.css +29 -0
  8. package/dist/index.js +297 -76
  9. package/dist/offline-reports.d.ts +4 -0
  10. package/dist/offline-reports.d.ts.map +1 -1
  11. package/dist/path-utils.d.ts +3 -0
  12. package/dist/path-utils.d.ts.map +1 -0
  13. package/dist/report-state.d.ts +3 -1
  14. package/dist/report-state.d.ts.map +1 -1
  15. package/dist/report-utils.d.ts.map +1 -1
  16. package/dist/reporter-utils.d.ts +0 -1
  17. package/dist/reporter-utils.d.ts.map +1 -1
  18. package/dist/reporter.cjs +232 -170
  19. package/dist/reporter.d.ts +1 -1
  20. package/dist/reporter.d.ts.map +1 -1
  21. package/dist/reporter.js +8 -6
  22. package/dist/schemas/http.d.ts +44 -0
  23. package/dist/schemas/http.d.ts.map +1 -0
  24. package/dist/schemas.d.ts +25 -6
  25. package/dist/schemas.d.ts.map +1 -1
  26. package/dist/server/app.d.ts +6 -0
  27. package/dist/server/app.d.ts.map +1 -1
  28. package/dist/server/artifact-routes.d.ts.map +1 -1
  29. package/dist/server/handlers.d.ts +3 -0
  30. package/dist/server/handlers.d.ts.map +1 -1
  31. package/dist/server/playwright-config.d.ts +2 -0
  32. package/dist/server/playwright-config.d.ts.map +1 -0
  33. package/dist/server/routes-context.d.ts +13 -0
  34. package/dist/server/routes-context.d.ts.map +1 -0
  35. package/dist/server/routes.d.ts +6 -1
  36. package/dist/server/routes.d.ts.map +1 -1
  37. package/dist/server/run-controller.d.ts +85 -0
  38. package/dist/server/run-controller.d.ts.map +1 -0
  39. package/dist/server/run-routes.d.ts +3 -0
  40. package/dist/server/run-routes.d.ts.map +1 -0
  41. package/dist/server.cjs +668 -286
  42. package/dist/server.js +2 -2
  43. package/dist/types.d.ts +6 -0
  44. package/dist/types.d.ts.map +1 -1
  45. 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 import_path5 = require("path");
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 import_path3 = require("path");
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 import_path2 = require("path");
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 = (0, import_path.isAbsolute)(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
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
- ...previousImage,
172
- source: previousImage.source ?? classifyImage(previousImage)
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
- return state.reportData.tests[id];
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
- state.reportData.tests = Object.fromEntries(
248
- Object.entries(state.reportData.tests).filter(([id]) => state.currentRunIds.has(id))
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 LocationSchema = import_zod.z.object({
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 VisualSourceSchema = import_zod.z.enum(["comparison", "baseline-only", "declared-only"]);
266
- var ImagesSchema = import_zod.z.object({
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 ScreenshotDeclarationSchema = import_zod.z.discriminatedUnion("kind", [
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
- visualName: import_zod.z.string(),
276
- kind: import_zod.z.literal("named"),
277
- declaredName: import_zod.z.string(),
278
- snapshotBaseName: import_zod.z.string(),
279
- occurrenceIndex: import_zod.z.number()
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
- visualName: import_zod.z.string(),
283
- kind: import_zod.z.literal("unnamed"),
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
- var AttachmentSchema = import_zod.z.object({
288
- name: import_zod.z.string(),
289
- path: import_zod.z.string(),
290
- contentType: import_zod.z.string()
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 TestStatusSchema = import_zod.z.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
293
- var TestResultStatusSchema = import_zod.z.enum(["failed", "success", "pending"]);
294
- var TestResultSchema = import_zod.z.object({
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: import_zod.z.number(),
297
- images: import_zod.z.record(import_zod.z.string(), ImagesSchema).optional(),
298
- visualDeclarations: import_zod.z.array(ScreenshotDeclarationSchema).optional(),
299
- error: import_zod.z.string().optional(),
300
- duration: import_zod.z.number().optional()
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 = import_zod.z.object({
303
- id: import_zod.z.string(),
304
- titlePath: import_zod.z.array(import_zod.z.string()),
305
- browser: import_zod.z.string(),
306
- projectName: import_zod.z.string().optional(),
307
- title: import_zod.z.string(),
308
- skip: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).optional(),
309
- retries: import_zod.z.number().optional(),
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: import_zod.z.array(TestResultSchema).optional(),
312
- approved: import_zod.z.record(import_zod.z.string(), import_zod.z.number()).nullable().optional(),
313
- attachments: import_zod.z.array(AttachmentSchema).optional(),
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: import_zod.z.boolean()
370
+ checked: import_zod2.z.boolean()
318
371
  });
319
- var CrvyRprtrSuiteSchema = import_zod.z.lazy(
320
- () => import_zod.z.object({
321
- path: import_zod.z.array(import_zod.z.string()),
322
- skip: import_zod.z.boolean(),
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: import_zod.z.boolean(),
325
- checked: import_zod.z.boolean(),
326
- indeterminate: import_zod.z.boolean(),
327
- children: import_zod.z.record(import_zod.z.string(), import_zod.z.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
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 = import_zod.z.object({
331
- type: import_zod.z.enum(["test-begin", "test-end", "run-end", "approve", "sync", "register"]),
332
- data: import_zod.z.unknown()
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 = import_zod.z.discriminatedUnion("type", [
335
- import_zod.z.object({ type: import_zod.z.literal("test-begin"), data: TestDataSchema }),
336
- import_zod.z.object({ type: import_zod.z.literal("test-update"), data: TestDataSchema }),
337
- import_zod.z.object({
338
- type: import_zod.z.literal("run-end"),
339
- data: import_zod.z.object({
340
- status: import_zod.z.enum(["passed", "failed", "skipped"]),
341
- removedTestIds: import_zod.z.array(import_zod.z.string())
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
- import_zod.z.object({
345
- type: import_zod.z.literal("sync"),
346
- data: import_zod.z.object({
347
- tests: import_zod.z.record(import_zod.z.string(), TestDataSchema),
348
- isUpdateMode: import_zod.z.boolean().optional()
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
- import_zod.z.object({ type: import_zod.z.literal("approve"), data: import_zod.z.unknown() })
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 = import_zod.z.object({
354
- id: import_zod.z.string(),
355
- title: import_zod.z.string(),
356
- titlePath: import_zod.z.array(import_zod.z.string()),
357
- browser: import_zod.z.string(),
358
- projectName: import_zod.z.string().optional(),
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 = import_zod.z.object({
362
- id: import_zod.z.string(),
363
- status: import_zod.z.enum(["passed", "failed", "skipped"]),
364
- attachments: import_zod.z.array(AttachmentSchema),
365
- visualNames: import_zod.z.array(import_zod.z.string()).default([]),
366
- visualDeclarations: import_zod.z.preprocess(
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
- import_zod.z.array(ScreenshotDeclarationSchema).optional()
427
+ import_zod2.z.array(ScreenshotDeclarationSchema).optional()
369
428
  ),
370
- error: import_zod.z.string().optional(),
371
- duration: import_zod.z.number().optional()
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 RegisterDataSchema = import_zod.z.object({
377
- playwrightSnapshotDir: import_zod.z.string().optional(),
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 ReportDataSchema = import_zod.z.object({
383
- isRunning: import_zod.z.boolean(),
384
- tests: import_zod.z.record(import_zod.z.string(), TestDataSchema),
385
- browsers: import_zod.z.array(import_zod.z.string()),
386
- isUpdateMode: import_zod.z.boolean(),
387
- screenshotDir: import_zod.z.string()
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 LoadedReportDataSchema = import_zod.z.object({
390
- tests: import_zod.z.record(import_zod.z.string(), TestDataSchema).optional(),
391
- isUpdateMode: import_zod.z.boolean().optional()
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 OfflineEventSchema = import_zod.z.object({
394
- type: import_zod.z.enum(["test-begin", "test-end", "run-end"]),
395
- data: import_zod.z.unknown(),
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 OfflineReportSchema = import_zod.z.object({
400
- version: import_zod.z.number(),
401
- generatedAt: import_zod.z.string(),
402
- workers: import_zod.z.number(),
403
- events: import_zod.z.array(OfflineEventSchema)
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 ApproveRequestBodySchema = import_zod.z.object({
406
- id: import_zod.z.string(),
407
- retry: import_zod.z.number(),
408
- image: import_zod.z.string()
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 = import_zod.z.object({
411
- tests: import_zod.z.record(import_zod.z.string(), TestDataSchema),
412
- isUpdateMode: import_zod.z.boolean().optional()
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 = import_zod.z.object({
472
+ var ClientBootstrapDataSchema = import_zod2.z.object({
415
473
  report: ReportApiResponseSchema.extend({
416
- isUpdateMode: import_zod.z.boolean()
474
+ isUpdateMode: import_zod2.z.boolean()
417
475
  }),
418
- liveUpdates: import_zod.z.boolean(),
419
- approvalEnabled: import_zod.z.boolean(),
420
- approvalMessage: import_zod.z.string().optional()
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 = import_zod.z.enum(["side-by-side", "swap", "slide", "blend"]);
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(import_path2.sep).join("/");
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, import_path2.dirname)((0, import_url.fileURLToPath)(import_meta.url));
473
- const candidates = [currentDir, (0, import_path2.resolve)(currentDir, "../dist")];
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, import_path2.resolve)(candidate, fileName);
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, import_path2.relative)((0, import_path2.dirname)(htmlPath), (0, import_path2.resolve)(screenshotDir)));
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, import_path2.resolve)(options.reportHtmlPath ?? DEFAULT_REPORT_HTML_PATH);
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, import_path2.dirname)(reportHtmlPath), { recursive: true });
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, import_path3.join)(testScreenshotDir, artifactPath);
632
+ const destPath = (0, import_path2.join)(testScreenshotDir, artifactPath);
573
633
  try {
574
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
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, import_path3.join)(screenshotDir, safeTestId);
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, import_path3.join)(testScreenshotDir, artifactPath);
594
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
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 import_path4 = require("path");
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, import_path4.extname)(filePath)) {
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, import_path4.extname)(filePath);
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, import_path4.resolve)(config.configDir, config.snapshotDir);
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, import_path4.relative)(input.config.testDir, input.testFile);
779
- const parsed = (0, import_path4.parse)(relativeTestFilePath);
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, import_path4.resolve)(input.config.configDir, snapshotPath);
857
+ return (0, import_path3.resolve)(input.config.configDir, snapshotPath);
798
858
  }
799
- function removeExtension(filePath, extension = (0, import_path4.extname)(filePath)) {
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, import_path4.extname)(declaredName) || ".png";
863
+ const extension = (0, import_path3.extname)(declaredName) || ".png";
804
864
  return {
805
865
  extension,
806
- filePath: (0, import_path4.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
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, import_path4.join)((0, import_path4.dirname)(occurrenceFilePath), (0, import_path4.basename)(occurrenceFilePath, extension));
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, import_path4.join)((0, import_path4.dirname)(anonymousFileName), (0, import_path4.basename)(anonymousFileName, extension));
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
- async onBegin(config, suite) {
924
- this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path5.dirname)(config.configFile);
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, import_path5.join)(this.screenshotDir, safeTestId);
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(