@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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,90 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.2] - 2026-07-03
|
|
9
|
+
|
|
10
|
+
### Miscellaneous
|
|
11
|
+
|
|
12
|
+
- Update repository URLs to creevey/rprtr
|
|
13
|
+
## [0.2.1] - 2026-07-03
|
|
14
|
+
## [0.2.0] - 2026-07-03
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **types:** Add run-status variant to ClientWebSocketMessage
|
|
19
|
+
- **reporter:** Include configFile and cwd in register message
|
|
20
|
+
- **server:** Add RunController for spawning playwright test
|
|
21
|
+
- **server:** Persist register payload configFile and cwd as runContext
|
|
22
|
+
- **server:** Instantiate RunController in createServerApp and dispose on close
|
|
23
|
+
- **server:** Add POST /api/run and /api/stop, surface runEnabled in /api/report
|
|
24
|
+
- **client:** Wire Start/Stop buttons to /api/run and /api/stop
|
|
25
|
+
- **client:** Add per-test and per-suite run buttons to sidebar tree
|
|
26
|
+
- **run-controller:** Resolve playwright via project package manager
|
|
27
|
+
- **run-controller:** Descriptor-based filter with positional translation
|
|
28
|
+
- **run-controller:** --test-list for suites with version fallback
|
|
29
|
+
- **client:** Descriptor filters, no-location feedback, isRunning guard
|
|
30
|
+
- **client:** Add captureTestStatuses/restoreTestStatuses helpers
|
|
31
|
+
- **client:** Revert orphaned pending on no-event run
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **schemas:** Extract HTTP request schemas to schemas/http.ts
|
|
36
|
+
- **schemas:** Use discriminatedUnion for Run/Stop responses
|
|
37
|
+
- **run-controller:** Document cwd param and test launch fallback
|
|
38
|
+
- **reporter:** Make onBegin synchronous and defer screenshotDir creation
|
|
39
|
+
- **client:** Reset runEventIds on rejected-start path
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
|
|
43
|
+
- **spec:** Run tests from UI design
|
|
44
|
+
- **plan:** Run tests from UI implementation plan
|
|
45
|
+
- **spec:** Run-from-UI robustness design
|
|
46
|
+
- **plan:** Run-from-UI robustness implementation plan
|
|
47
|
+
- **spec:** Stuck-pending run recovery design
|
|
48
|
+
- **plan:** Stuck-pending run recovery implementation plan
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **run-controller:** Guard against duplicate exit/error cleanup
|
|
53
|
+
- **reporter:** Honor CRVY_RPRTR_SERVER_URL env as fallback serverUrl
|
|
54
|
+
- **run-controller:** Override reporters and strip CI env for UI-triggered runs
|
|
55
|
+
- **run-from-ui:** Row height, re-run status, filtered-run scope, startup config
|
|
56
|
+
- **run-from-ui:** Mark tests pending on start, use file:line:column for precise re-runs
|
|
57
|
+
- **run-controller:** Resolve @crvy/rprtr from project cwd then server module
|
|
58
|
+
- **run-controller:** Clean up test-list temp file on spawn throw
|
|
59
|
+
- **knip:** Enable production-mode analysis in strict checks
|
|
60
|
+
|
|
61
|
+
### Styling
|
|
62
|
+
|
|
63
|
+
- **client:** Match semicolon convention and destructure location
|
|
64
|
+
- **docs:** Reformat CLI options table
|
|
65
|
+
|
|
66
|
+
### Testing
|
|
67
|
+
|
|
68
|
+
- **client:** Cover undefined-restore and empty/bare-root edge cases
|
|
69
|
+
## [0.1.4] - 2026-06-19
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **path-utils:** Add cross-platform absolute path helpers
|
|
74
|
+
- **server:** Log diagnostic when /file URL cannot resolve on host OS
|
|
75
|
+
|
|
76
|
+
### Documentation
|
|
77
|
+
|
|
78
|
+
- **plans:** Cross-OS path handling and stale actual URL on passing rerun
|
|
79
|
+
- Document cross-OS artifact loading limitations
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- **report-utils:** Route Windows-style absolute paths to /file on any host
|
|
84
|
+
- **server:** Check pre-resolve path for foreign-OS diagnostic
|
|
85
|
+
- **report-state:** Drop stale comparison actual URL on passed reruns
|
|
86
|
+
- **report-state:** Strip stale actual/diff when preserving baseline-only images
|
|
87
|
+
|
|
88
|
+
### Testing
|
|
89
|
+
|
|
90
|
+
- **path-utils:** Cover forward-slash UNC form
|
|
91
|
+
- **server-routes:** Cover baseline enrichment after failed→passed progression
|
|
8
92
|
## [0.1.3] - 2026-06-18
|
|
9
93
|
|
|
10
94
|
### Fixed
|
package/README.md
CHANGED
|
@@ -74,13 +74,14 @@ If `artifact-dir` is provided, the CLI treats it as the directory containing:
|
|
|
74
74
|
|
|
75
75
|
Explicit flags override the paths derived from `artifact-dir`.
|
|
76
76
|
|
|
77
|
-
| Option | Short | Default | Description
|
|
78
|
-
| ------------------ | ----- | --------------- |
|
|
79
|
-
| `--port` | `-p` | `3000` | Server port
|
|
80
|
-
| `--screenshot-dir` | `-s` | `./screenshots` | Screenshot directory path
|
|
81
|
-
| `--report-path` | `-r` | `./report.json` | Report JSON file path or directory containing `report.json` and `crvy-rprtr-*.json` files
|
|
77
|
+
| Option | Short | Default | Description |
|
|
78
|
+
| ------------------ | ----- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
79
|
+
| `--port` | `-p` | `3000` | Server port |
|
|
80
|
+
| `--screenshot-dir` | `-s` | `./screenshots` | Screenshot directory path |
|
|
81
|
+
| `--report-path` | `-r` | `./report.json` | Report JSON file path or directory containing `report.json` and `crvy-rprtr-*.json` files |
|
|
82
|
+
| `--config` | `-c` | auto-detect | Playwright config path used to enable the run buttons at startup. When omitted, the server discovers `playwright.config.*` in the working directory. A registering reporter always overrides this. |
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
When the server can resolve a Playwright config (via `--config` or auto-discovery, or once a reporter registers), the sidebar shows Start/Stop and per-test run buttons that trigger `playwright test` without leaving the browser. Approval-routing resolver overrides are available through the programmatic server API, not additional CLI flags.
|
|
84
85
|
|
|
85
86
|
## How It Works
|
|
86
87
|
|
|
@@ -113,6 +114,14 @@ Approval routing uses the same resolver, but it reads its resolver settings from
|
|
|
113
114
|
|
|
114
115
|
When the server is running, Crvy Rprtr also refreshes the UI after report JSON or screenshot artifacts change on disk.
|
|
115
116
|
|
|
117
|
+
## Cross-OS Artifact Loading
|
|
118
|
+
|
|
119
|
+
Crvy Rprtr stores image URLs exactly as the reporter that produced them saw them. In live mode (server running during the test run), failure artifacts are referenced by absolute path under `/file/<encoded>`; in CI/offline mode, attachments are copied into `screenshots/` and referenced by relative path under `/screenshots/`.
|
|
120
|
+
|
|
121
|
+
If you generate a report on one operating system and then open it on another (for example, downloading a Windows CI runner's `report.json` onto a macOS laptop), absolute-path `/file/...` URLs cannot resolve: the file is not on your filesystem. Crvy Rprtr logs a single diagnostic line per such request and returns 404. The `/screenshots/...` and `/baseline/...` URLs remain portable because they resolve through the server's `screenshotDir` or snapshot resolver.
|
|
122
|
+
|
|
123
|
+
For fully portable artifact loading across operating systems, run the reporter in CI mode (`ci: true`) and ship the `screenshots/` directory alongside the report JSON.
|
|
124
|
+
|
|
116
125
|
## Programmatic API
|
|
117
126
|
|
|
118
127
|
```ts
|
|
@@ -170,169 +170,211 @@ function resolveBaselineTargets(input) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// src/schemas.ts
|
|
173
|
+
import { z as z2 } from "zod";
|
|
174
|
+
|
|
175
|
+
// src/schemas/http.ts
|
|
173
176
|
import { z } from "zod";
|
|
174
|
-
var
|
|
177
|
+
var ApproveRequestBodySchema = z.object({
|
|
178
|
+
id: z.string(),
|
|
179
|
+
retry: z.number(),
|
|
180
|
+
image: z.string()
|
|
181
|
+
});
|
|
182
|
+
var RunTestDescriptorSchema = z.object({
|
|
175
183
|
file: z.string(),
|
|
176
|
-
line: z.number()
|
|
184
|
+
line: z.number(),
|
|
185
|
+
column: z.number().optional(),
|
|
186
|
+
projectName: z.string().optional(),
|
|
187
|
+
titlePath: z.array(z.string())
|
|
177
188
|
});
|
|
178
|
-
var
|
|
179
|
-
|
|
180
|
-
actual: z.string().optional(),
|
|
181
|
-
expect: z.string().optional(),
|
|
182
|
-
diff: z.string().optional(),
|
|
183
|
-
error: z.string().optional(),
|
|
184
|
-
source: VisualSourceSchema.optional()
|
|
189
|
+
var RunRequestBodySchema = z.object({
|
|
190
|
+
tests: z.array(RunTestDescriptorSchema).optional()
|
|
185
191
|
});
|
|
186
|
-
var
|
|
192
|
+
var RunResponseSchema = z.discriminatedUnion("ok", [
|
|
193
|
+
z.object({ ok: z.literal(true) }),
|
|
187
194
|
z.object({
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}),
|
|
195
|
+
ok: z.literal(false),
|
|
196
|
+
reason: z.enum(["no-config", "already-running", "no-tests"])
|
|
197
|
+
})
|
|
198
|
+
]);
|
|
199
|
+
var StopResponseSchema = z.discriminatedUnion("ok", [
|
|
200
|
+
z.object({ ok: z.literal(true) }),
|
|
194
201
|
z.object({
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
occurrenceIndex: z.number()
|
|
202
|
+
ok: z.literal(false),
|
|
203
|
+
reason: z.literal("not-running")
|
|
198
204
|
})
|
|
199
205
|
]);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
|
|
207
|
+
// src/schemas.ts
|
|
208
|
+
var LocationSchema = z2.object({
|
|
209
|
+
file: z2.string(),
|
|
210
|
+
line: z2.number(),
|
|
211
|
+
column: z2.number().optional()
|
|
212
|
+
});
|
|
213
|
+
var VisualSourceSchema = z2.enum(["comparison", "baseline-only", "declared-only"]);
|
|
214
|
+
var ImagesSchema = z2.object({
|
|
215
|
+
actual: z2.string().optional(),
|
|
216
|
+
expect: z2.string().optional(),
|
|
217
|
+
diff: z2.string().optional(),
|
|
218
|
+
error: z2.string().optional(),
|
|
219
|
+
source: VisualSourceSchema.optional()
|
|
204
220
|
});
|
|
205
|
-
var
|
|
206
|
-
|
|
207
|
-
|
|
221
|
+
var ScreenshotDeclarationSchema = z2.discriminatedUnion("kind", [
|
|
222
|
+
z2.object({
|
|
223
|
+
visualName: z2.string(),
|
|
224
|
+
kind: z2.literal("named"),
|
|
225
|
+
declaredName: z2.string(),
|
|
226
|
+
snapshotBaseName: z2.string(),
|
|
227
|
+
occurrenceIndex: z2.number()
|
|
228
|
+
}),
|
|
229
|
+
z2.object({
|
|
230
|
+
visualName: z2.string(),
|
|
231
|
+
kind: z2.literal("unnamed"),
|
|
232
|
+
occurrenceIndex: z2.number()
|
|
233
|
+
})
|
|
234
|
+
]);
|
|
235
|
+
var AttachmentSchema = z2.object({
|
|
236
|
+
name: z2.string(),
|
|
237
|
+
path: z2.string(),
|
|
238
|
+
contentType: z2.string()
|
|
239
|
+
});
|
|
240
|
+
var TestStatusSchema = z2.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
|
|
241
|
+
var TestResultStatusSchema = z2.enum(["failed", "success", "pending"]);
|
|
242
|
+
var TestResultSchema = z2.object({
|
|
208
243
|
status: TestResultStatusSchema,
|
|
209
|
-
retries:
|
|
210
|
-
images:
|
|
211
|
-
visualDeclarations:
|
|
212
|
-
error:
|
|
213
|
-
duration:
|
|
244
|
+
retries: z2.number(),
|
|
245
|
+
images: z2.record(z2.string(), ImagesSchema).optional(),
|
|
246
|
+
visualDeclarations: z2.array(ScreenshotDeclarationSchema).optional(),
|
|
247
|
+
error: z2.string().optional(),
|
|
248
|
+
duration: z2.number().optional()
|
|
214
249
|
});
|
|
215
|
-
var TestDataSchema =
|
|
216
|
-
id:
|
|
217
|
-
titlePath:
|
|
218
|
-
browser:
|
|
219
|
-
projectName:
|
|
220
|
-
title:
|
|
221
|
-
skip:
|
|
222
|
-
retries:
|
|
250
|
+
var TestDataSchema = z2.object({
|
|
251
|
+
id: z2.string(),
|
|
252
|
+
titlePath: z2.array(z2.string()),
|
|
253
|
+
browser: z2.string(),
|
|
254
|
+
projectName: z2.string().optional(),
|
|
255
|
+
title: z2.string(),
|
|
256
|
+
skip: z2.union([z2.boolean(), z2.string()]).optional(),
|
|
257
|
+
retries: z2.number().optional(),
|
|
223
258
|
status: TestStatusSchema.optional(),
|
|
224
|
-
results:
|
|
225
|
-
approved:
|
|
226
|
-
attachments:
|
|
259
|
+
results: z2.array(TestResultSchema).optional(),
|
|
260
|
+
approved: z2.record(z2.string(), z2.number()).nullable().optional(),
|
|
261
|
+
attachments: z2.array(AttachmentSchema).optional(),
|
|
227
262
|
location: LocationSchema.optional()
|
|
228
263
|
});
|
|
229
264
|
var CrvyRprtrTestSchema = TestDataSchema.extend({
|
|
230
|
-
checked:
|
|
265
|
+
checked: z2.boolean()
|
|
231
266
|
});
|
|
232
|
-
var CrvyRprtrSuiteSchema =
|
|
233
|
-
() =>
|
|
234
|
-
path:
|
|
235
|
-
skip:
|
|
267
|
+
var CrvyRprtrSuiteSchema = z2.lazy(
|
|
268
|
+
() => z2.object({
|
|
269
|
+
path: z2.array(z2.string()),
|
|
270
|
+
skip: z2.boolean(),
|
|
236
271
|
status: TestStatusSchema.optional(),
|
|
237
|
-
opened:
|
|
238
|
-
checked:
|
|
239
|
-
indeterminate:
|
|
240
|
-
children:
|
|
272
|
+
opened: z2.boolean(),
|
|
273
|
+
checked: z2.boolean(),
|
|
274
|
+
indeterminate: z2.boolean(),
|
|
275
|
+
children: z2.record(z2.string(), z2.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
|
|
241
276
|
})
|
|
242
277
|
);
|
|
243
|
-
var IncomingWebSocketMessageSchema =
|
|
244
|
-
type:
|
|
245
|
-
data:
|
|
278
|
+
var IncomingWebSocketMessageSchema = z2.object({
|
|
279
|
+
type: z2.enum(["test-begin", "test-end", "run-end", "approve", "sync", "register"]),
|
|
280
|
+
data: z2.unknown()
|
|
246
281
|
});
|
|
247
|
-
var WebSocketMessageSchema =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
type:
|
|
252
|
-
data:
|
|
253
|
-
status:
|
|
254
|
-
removedTestIds:
|
|
282
|
+
var WebSocketMessageSchema = z2.discriminatedUnion("type", [
|
|
283
|
+
z2.object({ type: z2.literal("test-begin"), data: TestDataSchema }),
|
|
284
|
+
z2.object({ type: z2.literal("test-update"), data: TestDataSchema }),
|
|
285
|
+
z2.object({
|
|
286
|
+
type: z2.literal("run-end"),
|
|
287
|
+
data: z2.object({
|
|
288
|
+
status: z2.enum(["passed", "failed", "skipped"]),
|
|
289
|
+
removedTestIds: z2.array(z2.string())
|
|
255
290
|
})
|
|
256
291
|
}),
|
|
257
|
-
|
|
258
|
-
type:
|
|
259
|
-
data:
|
|
260
|
-
tests:
|
|
261
|
-
isUpdateMode:
|
|
292
|
+
z2.object({
|
|
293
|
+
type: z2.literal("sync"),
|
|
294
|
+
data: z2.object({
|
|
295
|
+
tests: z2.record(z2.string(), TestDataSchema),
|
|
296
|
+
isUpdateMode: z2.boolean().optional()
|
|
262
297
|
})
|
|
263
298
|
}),
|
|
264
|
-
|
|
299
|
+
z2.object({ type: z2.literal("approve"), data: z2.unknown() }),
|
|
300
|
+
z2.object({
|
|
301
|
+
type: z2.literal("run-status"),
|
|
302
|
+
data: z2.object({
|
|
303
|
+
running: z2.boolean()
|
|
304
|
+
})
|
|
305
|
+
})
|
|
265
306
|
]);
|
|
266
|
-
var TestBeginDataSchema =
|
|
267
|
-
id:
|
|
268
|
-
title:
|
|
269
|
-
titlePath:
|
|
270
|
-
browser:
|
|
271
|
-
projectName:
|
|
307
|
+
var TestBeginDataSchema = z2.object({
|
|
308
|
+
id: z2.string(),
|
|
309
|
+
title: z2.string(),
|
|
310
|
+
titlePath: z2.array(z2.string()),
|
|
311
|
+
browser: z2.string(),
|
|
312
|
+
projectName: z2.string().optional(),
|
|
272
313
|
location: LocationSchema
|
|
273
314
|
});
|
|
274
|
-
var TestEndDataSchema =
|
|
275
|
-
id:
|
|
276
|
-
status:
|
|
277
|
-
attachments:
|
|
278
|
-
visualNames:
|
|
279
|
-
visualDeclarations:
|
|
315
|
+
var TestEndDataSchema = z2.object({
|
|
316
|
+
id: z2.string(),
|
|
317
|
+
status: z2.enum(["passed", "failed", "skipped"]),
|
|
318
|
+
attachments: z2.array(AttachmentSchema),
|
|
319
|
+
visualNames: z2.array(z2.string()).default([]),
|
|
320
|
+
visualDeclarations: z2.preprocess(
|
|
280
321
|
(value) => value === null ? void 0 : value,
|
|
281
|
-
|
|
322
|
+
z2.array(ScreenshotDeclarationSchema).optional()
|
|
282
323
|
),
|
|
283
|
-
error:
|
|
284
|
-
duration:
|
|
324
|
+
error: z2.string().optional(),
|
|
325
|
+
duration: z2.number().optional()
|
|
285
326
|
});
|
|
286
|
-
var RunEndDataSchema =
|
|
287
|
-
status:
|
|
327
|
+
var RunEndDataSchema = z2.object({
|
|
328
|
+
status: z2.enum(["passed", "failed", "skipped"])
|
|
288
329
|
});
|
|
289
|
-
var RegisterDataSchema =
|
|
290
|
-
playwrightSnapshotDir:
|
|
291
|
-
playwrightTestDir:
|
|
292
|
-
playwrightSnapshotPathTemplate:
|
|
293
|
-
playwrightToHaveScreenshotPathTemplate:
|
|
330
|
+
var RegisterDataSchema = z2.object({
|
|
331
|
+
playwrightSnapshotDir: z2.string().optional(),
|
|
332
|
+
playwrightTestDir: z2.string().optional(),
|
|
333
|
+
playwrightSnapshotPathTemplate: z2.string().optional(),
|
|
334
|
+
playwrightToHaveScreenshotPathTemplate: z2.string().optional(),
|
|
335
|
+
configFile: z2.string().optional(),
|
|
336
|
+
cwd: z2.string().optional()
|
|
294
337
|
});
|
|
295
|
-
var ReportDataSchema =
|
|
296
|
-
isRunning:
|
|
297
|
-
tests:
|
|
298
|
-
browsers:
|
|
299
|
-
isUpdateMode:
|
|
300
|
-
screenshotDir:
|
|
338
|
+
var ReportDataSchema = z2.object({
|
|
339
|
+
isRunning: z2.boolean(),
|
|
340
|
+
tests: z2.record(z2.string(), TestDataSchema),
|
|
341
|
+
browsers: z2.array(z2.string()),
|
|
342
|
+
isUpdateMode: z2.boolean(),
|
|
343
|
+
screenshotDir: z2.string()
|
|
301
344
|
});
|
|
302
|
-
var LoadedReportDataSchema =
|
|
303
|
-
tests:
|
|
304
|
-
isUpdateMode:
|
|
345
|
+
var LoadedReportDataSchema = z2.object({
|
|
346
|
+
tests: z2.record(z2.string(), TestDataSchema).optional(),
|
|
347
|
+
isUpdateMode: z2.boolean().optional()
|
|
305
348
|
});
|
|
306
|
-
var OfflineEventSchema =
|
|
307
|
-
type:
|
|
308
|
-
data:
|
|
309
|
-
timestamp:
|
|
310
|
-
workerIndex:
|
|
349
|
+
var OfflineEventSchema = z2.object({
|
|
350
|
+
type: z2.enum(["test-begin", "test-end", "run-end"]),
|
|
351
|
+
data: z2.unknown(),
|
|
352
|
+
timestamp: z2.number(),
|
|
353
|
+
workerIndex: z2.number()
|
|
311
354
|
});
|
|
312
|
-
var OfflineReportSchema =
|
|
313
|
-
version:
|
|
314
|
-
generatedAt:
|
|
315
|
-
workers:
|
|
316
|
-
events:
|
|
317
|
-
});
|
|
318
|
-
var ApproveRequestBodySchema = z.object({
|
|
319
|
-
id: z.string(),
|
|
320
|
-
retry: z.number(),
|
|
321
|
-
image: z.string()
|
|
355
|
+
var OfflineReportSchema = z2.object({
|
|
356
|
+
version: z2.number(),
|
|
357
|
+
generatedAt: z2.string(),
|
|
358
|
+
workers: z2.number(),
|
|
359
|
+
events: z2.array(OfflineEventSchema)
|
|
322
360
|
});
|
|
323
|
-
var ReportApiResponseSchema =
|
|
324
|
-
tests:
|
|
325
|
-
isUpdateMode:
|
|
361
|
+
var ReportApiResponseSchema = z2.object({
|
|
362
|
+
tests: z2.record(z2.string(), TestDataSchema),
|
|
363
|
+
isUpdateMode: z2.boolean().optional(),
|
|
364
|
+
isRunning: z2.boolean().optional(),
|
|
365
|
+
runEnabled: z2.boolean().optional()
|
|
326
366
|
});
|
|
327
|
-
var ClientBootstrapDataSchema =
|
|
367
|
+
var ClientBootstrapDataSchema = z2.object({
|
|
328
368
|
report: ReportApiResponseSchema.extend({
|
|
329
|
-
isUpdateMode:
|
|
369
|
+
isUpdateMode: z2.boolean()
|
|
330
370
|
}),
|
|
331
|
-
liveUpdates:
|
|
332
|
-
approvalEnabled:
|
|
333
|
-
approvalMessage:
|
|
371
|
+
liveUpdates: z2.boolean(),
|
|
372
|
+
approvalEnabled: z2.boolean(),
|
|
373
|
+
approvalMessage: z2.string().optional(),
|
|
374
|
+
runEnabled: z2.boolean().optional(),
|
|
375
|
+
isRunning: z2.boolean().optional()
|
|
334
376
|
});
|
|
335
|
-
var ImagesViewModeSchema =
|
|
377
|
+
var ImagesViewModeSchema = z2.enum(["side-by-side", "swap", "slide", "blend"]);
|
|
336
378
|
function safeParse(schema, data) {
|
|
337
379
|
const result = schema.safeParse(data);
|
|
338
380
|
if (result.success) {
|
|
@@ -341,8 +383,26 @@ function safeParse(schema, data) {
|
|
|
341
383
|
return null;
|
|
342
384
|
}
|
|
343
385
|
|
|
386
|
+
// src/path-utils.ts
|
|
387
|
+
var posixAbsolutePattern = /^\//u;
|
|
388
|
+
var windowsDrivePattern = /^[A-Za-z]:[\\/]/u;
|
|
389
|
+
var windowsUncPattern = /^[\\/][\\/]/u;
|
|
390
|
+
function isPosixAbsolutePath(p) {
|
|
391
|
+
return posixAbsolutePattern.test(p);
|
|
392
|
+
}
|
|
393
|
+
function isWindowsAbsolutePath(p) {
|
|
394
|
+
return windowsDrivePattern.test(p) || windowsUncPattern.test(p);
|
|
395
|
+
}
|
|
396
|
+
function isAnyAbsolutePath(p) {
|
|
397
|
+
return isPosixAbsolutePath(p) || isWindowsAbsolutePath(p);
|
|
398
|
+
}
|
|
399
|
+
function isForeignAbsolutePath(p, hostPlatform) {
|
|
400
|
+
if (!isAnyAbsolutePath(p)) return false;
|
|
401
|
+
if (hostPlatform === "win32") return !isWindowsAbsolutePath(p);
|
|
402
|
+
return !isPosixAbsolutePath(p);
|
|
403
|
+
}
|
|
404
|
+
|
|
344
405
|
// src/report-utils.ts
|
|
345
|
-
import { isAbsolute } from "path";
|
|
346
406
|
function normalizeScreenshotsBaseUrl(baseUrl) {
|
|
347
407
|
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
348
408
|
}
|
|
@@ -387,7 +447,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
387
447
|
const role = match[2];
|
|
388
448
|
if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
|
|
389
449
|
images[baseName] ??= {};
|
|
390
|
-
const url =
|
|
450
|
+
const url = isAnyAbsolutePath(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
|
|
391
451
|
const img = images[baseName];
|
|
392
452
|
if (img !== null && img !== void 0) {
|
|
393
453
|
if (role === "actual") img.actual = url;
|
|
@@ -422,9 +482,6 @@ function isCurrentArtifact(image) {
|
|
|
422
482
|
}
|
|
423
483
|
function isReusablePassingImage(image) {
|
|
424
484
|
const source = image.source ?? classifyImage(image);
|
|
425
|
-
if (source === "comparison") {
|
|
426
|
-
return image.actual !== void 0 && image.diff === void 0;
|
|
427
|
-
}
|
|
428
485
|
return source === "baseline-only";
|
|
429
486
|
}
|
|
430
487
|
function hasReusablePassingImages(images) {
|
|
@@ -445,8 +502,8 @@ function preservePreviousPassingImages(test, status, images) {
|
|
|
445
502
|
return {
|
|
446
503
|
...currentImages,
|
|
447
504
|
[name]: {
|
|
448
|
-
|
|
449
|
-
source:
|
|
505
|
+
expect: previousImage.expect,
|
|
506
|
+
source: "baseline-only"
|
|
450
507
|
}
|
|
451
508
|
};
|
|
452
509
|
}, images);
|
|
@@ -469,7 +526,12 @@ function createMutableReportState(screenshotDir = "./screenshots") {
|
|
|
469
526
|
function applyTestBeginEvent(state, data) {
|
|
470
527
|
const { id, title, titlePath, browser, projectName, location } = data;
|
|
471
528
|
state.currentRunIds.add(id);
|
|
472
|
-
state.reportData.tests[id]
|
|
529
|
+
const existing = state.reportData.tests[id];
|
|
530
|
+
if (existing !== void 0) {
|
|
531
|
+
existing.status = "running";
|
|
532
|
+
return existing;
|
|
533
|
+
}
|
|
534
|
+
const created = {
|
|
473
535
|
id,
|
|
474
536
|
titlePath: titlePath ?? [],
|
|
475
537
|
browser: browser ?? "",
|
|
@@ -481,7 +543,8 @@ function applyTestBeginEvent(state, data) {
|
|
|
481
543
|
location,
|
|
482
544
|
status: "running"
|
|
483
545
|
};
|
|
484
|
-
|
|
546
|
+
state.reportData.tests[id] = created;
|
|
547
|
+
return created;
|
|
485
548
|
}
|
|
486
549
|
function applyTestEndEvent(state, data, options = {}) {
|
|
487
550
|
const test = state.reportData.tests[data.id];
|
|
@@ -519,11 +582,13 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
519
582
|
diffCount
|
|
520
583
|
};
|
|
521
584
|
}
|
|
522
|
-
function finalizeRunEvent(state) {
|
|
585
|
+
function finalizeRunEvent(state, options = {}) {
|
|
523
586
|
state.reportData.isRunning = false;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
587
|
+
if (options.preserveNonCurrent !== true) {
|
|
588
|
+
state.reportData.tests = Object.fromEntries(
|
|
589
|
+
Object.entries(state.reportData.tests).filter(([id]) => state.currentRunIds.has(id))
|
|
590
|
+
);
|
|
591
|
+
}
|
|
527
592
|
state.currentRunIds.clear();
|
|
528
593
|
const runTests = Object.values(state.reportData.tests).filter((test) => test !== void 0);
|
|
529
594
|
return {
|
|
@@ -534,10 +599,13 @@ function finalizeRunEvent(state) {
|
|
|
534
599
|
}
|
|
535
600
|
|
|
536
601
|
export {
|
|
602
|
+
isForeignAbsolutePath,
|
|
537
603
|
createMutableReportState,
|
|
538
604
|
applyTestBeginEvent,
|
|
539
605
|
applyTestEndEvent,
|
|
540
606
|
finalizeRunEvent,
|
|
607
|
+
ApproveRequestBodySchema,
|
|
608
|
+
RunRequestBodySchema,
|
|
541
609
|
IncomingWebSocketMessageSchema,
|
|
542
610
|
TestBeginDataSchema,
|
|
543
611
|
TestEndDataSchema,
|
|
@@ -545,7 +613,6 @@ export {
|
|
|
545
613
|
RegisterDataSchema,
|
|
546
614
|
LoadedReportDataSchema,
|
|
547
615
|
OfflineReportSchema,
|
|
548
|
-
ApproveRequestBodySchema,
|
|
549
616
|
ClientBootstrapDataSchema,
|
|
550
617
|
safeParse,
|
|
551
618
|
withResolvedVisualNames,
|