@crvy/rprtr 0.0.3

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 (50) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/dist/chunk-BRFA7JCQ.js +729 -0
  5. package/dist/chunk-IEYNAB6M.js +263 -0
  6. package/dist/cli.d.ts +10 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +61 -0
  9. package/dist/index.css +1335 -0
  10. package/dist/index.html +14 -0
  11. package/dist/index.js +21106 -0
  12. package/dist/is-direct-execution.d.ts +2 -0
  13. package/dist/is-direct-execution.d.ts.map +1 -0
  14. package/dist/offline-reports.d.ts +9 -0
  15. package/dist/offline-reports.d.ts.map +1 -0
  16. package/dist/report-artifact.d.ts +12 -0
  17. package/dist/report-artifact.d.ts.map +1 -0
  18. package/dist/report-state.d.ts +28 -0
  19. package/dist/report-state.d.ts.map +1 -0
  20. package/dist/report-utils.d.ts +5 -0
  21. package/dist/report-utils.d.ts.map +1 -0
  22. package/dist/reporter-utils.d.ts +3 -0
  23. package/dist/reporter-utils.d.ts.map +1 -0
  24. package/dist/reporter.d.ts +34 -0
  25. package/dist/reporter.d.ts.map +1 -0
  26. package/dist/reporter.js +393 -0
  27. package/dist/schemas.d.ts +380 -0
  28. package/dist/schemas.d.ts.map +1 -0
  29. package/dist/server/app.d.ts +16 -0
  30. package/dist/server/app.d.ts.map +1 -0
  31. package/dist/server/bun-adapter.d.ts +3 -0
  32. package/dist/server/bun-adapter.d.ts.map +1 -0
  33. package/dist/server/file-utils.d.ts +7 -0
  34. package/dist/server/file-utils.d.ts.map +1 -0
  35. package/dist/server/handlers.d.ts +21 -0
  36. package/dist/server/handlers.d.ts.map +1 -0
  37. package/dist/server/node-adapter.d.ts +3 -0
  38. package/dist/server/node-adapter.d.ts.map +1 -0
  39. package/dist/server/routes.d.ts +16 -0
  40. package/dist/server/routes.d.ts.map +1 -0
  41. package/dist/server/utils.d.ts +3 -0
  42. package/dist/server/utils.d.ts.map +1 -0
  43. package/dist/server/ws.d.ts +4 -0
  44. package/dist/server/ws.d.ts.map +1 -0
  45. package/dist/server.d.ts +4 -0
  46. package/dist/server.d.ts.map +1 -0
  47. package/dist/server.js +7 -0
  48. package/dist/types.d.ts +116 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/package.json +102 -0
@@ -0,0 +1,263 @@
1
+ // src/report-utils.ts
2
+ function normalizeScreenshotsBaseUrl(baseUrl) {
3
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
4
+ }
5
+ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/") {
6
+ const images = {};
7
+ const baseUrl = normalizeScreenshotsBaseUrl(screenshotsBaseUrl);
8
+ for (const attachment of attachments) {
9
+ if (attachment.contentType !== "image/png") continue;
10
+ const match = attachment.name.match(/^(.+?)-(actual|expected|diff)(?:\.png)?$/);
11
+ if (match === null) continue;
12
+ const baseName = match[1];
13
+ const role = match[2];
14
+ if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
15
+ images[baseName] ??= { actual: "" };
16
+ const url = `${baseUrl}${attachment.path}`;
17
+ const img = images[baseName];
18
+ if (img !== null && img !== void 0) {
19
+ if (role === "actual") img.actual = url;
20
+ else if (role === "expected") img.expect = url;
21
+ else if (role === "diff") img.diff = url;
22
+ }
23
+ }
24
+ for (const key of Object.keys(images)) {
25
+ const img = images[key];
26
+ if (img?.actual !== null && img?.actual !== void 0 && img?.expect !== null && img?.expect !== void 0 && img?.diff === void 0)
27
+ delete img.expect;
28
+ }
29
+ return images;
30
+ }
31
+ function mapStatus(status) {
32
+ switch (status) {
33
+ case "passed":
34
+ return "success";
35
+ case "failed":
36
+ return "failed";
37
+ case "skipped":
38
+ return "pending";
39
+ default:
40
+ return "unknown";
41
+ }
42
+ }
43
+
44
+ // src/report-state.ts
45
+ function hasReviewablePassingImages(images) {
46
+ return Object.values(images).some(
47
+ (img) => img !== null && img !== void 0 && img.actual !== null && img.actual !== void 0 && img.diff === void 0
48
+ );
49
+ }
50
+ function preservePreviousPassingImages(test, status, images) {
51
+ if (status !== "passed" || Object.keys(images).length > 0) {
52
+ return images;
53
+ }
54
+ const previousImages = test.results?.[0]?.images ?? {};
55
+ return hasReviewablePassingImages(previousImages) ? previousImages : images;
56
+ }
57
+ function countDiffImages(images) {
58
+ return Object.values(images).filter((img) => img?.diff !== null && img?.diff !== void 0).length;
59
+ }
60
+ function createMutableReportState(screenshotDir = "./screenshots") {
61
+ return {
62
+ reportData: {
63
+ isRunning: false,
64
+ tests: {},
65
+ browsers: ["chromium"],
66
+ isUpdateMode: false,
67
+ screenshotDir
68
+ },
69
+ currentRunIds: /* @__PURE__ */ new Set()
70
+ };
71
+ }
72
+ function applyTestBeginEvent(state, data) {
73
+ const { id, title, titlePath, browser, location } = data;
74
+ state.currentRunIds.add(id);
75
+ state.reportData.tests[id] ??= {
76
+ id,
77
+ titlePath: titlePath ?? [],
78
+ browser: browser ?? "",
79
+ title: title ?? "",
80
+ location,
81
+ status: "running"
82
+ };
83
+ return state.reportData.tests[id];
84
+ }
85
+ function applyTestEndEvent(state, data, options = {}) {
86
+ const test = state.reportData.tests[data.id];
87
+ if (test === void 0) {
88
+ return null;
89
+ }
90
+ test.status = mapStatus(data.status);
91
+ const images = preservePreviousPassingImages(
92
+ test,
93
+ data.status,
94
+ attachmentsToImages(data.attachments, options.screenshotsBaseUrl)
95
+ );
96
+ const diffCount = countDiffImages(images);
97
+ const hasDiffs = diffCount > 0;
98
+ if (hasDiffs) {
99
+ test.approved = null;
100
+ }
101
+ test.results = [
102
+ {
103
+ status: data.status === "passed" ? "success" : "failed",
104
+ retries: 0,
105
+ images,
106
+ error: data.error,
107
+ duration: data.duration
108
+ }
109
+ ];
110
+ return {
111
+ test,
112
+ diffCount
113
+ };
114
+ }
115
+ function finalizeRunEvent(state) {
116
+ state.reportData.isRunning = false;
117
+ state.reportData.tests = Object.fromEntries(
118
+ Object.entries(state.reportData.tests).filter(([id]) => state.currentRunIds.has(id))
119
+ );
120
+ state.currentRunIds.clear();
121
+ const runTests = Object.values(state.reportData.tests).filter((test) => test !== void 0);
122
+ return {
123
+ passed: runTests.filter((test) => test.status === "success").length,
124
+ failed: runTests.filter((test) => test.status === "failed").length,
125
+ pending: runTests.filter((test) => test.status === "pending").length
126
+ };
127
+ }
128
+
129
+ // src/schemas.ts
130
+ import { z } from "zod";
131
+ var LocationSchema = z.object({
132
+ file: z.string(),
133
+ line: z.number()
134
+ });
135
+ var ImagesSchema = z.object({
136
+ actual: z.string(),
137
+ expect: z.string().optional(),
138
+ diff: z.string().optional(),
139
+ error: z.string().optional()
140
+ });
141
+ var AttachmentSchema = z.object({
142
+ name: z.string(),
143
+ path: z.string(),
144
+ contentType: z.string()
145
+ });
146
+ var TestStatusSchema = z.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
147
+ var TestResultSchema = z.object({
148
+ status: z.enum(["failed", "success"]),
149
+ retries: z.number(),
150
+ // eslint-disable-next-line typescript/no-unsafe-type-assertion
151
+ images: z.record(z.string(), ImagesSchema).optional(),
152
+ error: z.string().optional(),
153
+ duration: z.number().optional()
154
+ });
155
+ var TestDataSchema = z.object({
156
+ id: z.string(),
157
+ titlePath: z.array(z.string()),
158
+ browser: z.string(),
159
+ title: z.string(),
160
+ skip: z.union([z.boolean(), z.string()]).optional(),
161
+ retries: z.number().optional(),
162
+ status: TestStatusSchema.optional(),
163
+ results: z.array(TestResultSchema).optional(),
164
+ approved: z.record(z.string(), z.number()).nullable().optional(),
165
+ attachments: z.array(AttachmentSchema).optional(),
166
+ location: LocationSchema.optional()
167
+ });
168
+ var CrvyRprtrTestSchema = TestDataSchema.extend({
169
+ checked: z.boolean()
170
+ });
171
+ var CrvyRprtrSuiteSchema = z.lazy(
172
+ () => z.object({
173
+ path: z.array(z.string()),
174
+ skip: z.boolean(),
175
+ status: TestStatusSchema.optional(),
176
+ opened: z.boolean(),
177
+ checked: z.boolean(),
178
+ indeterminate: z.boolean(),
179
+ // eslint-disable-next-line typescript/no-unsafe-type-assertion
180
+ children: z.record(z.string(), z.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
181
+ })
182
+ );
183
+ var WebSocketMessageSchema = z.object({
184
+ type: z.enum(["test-begin", "test-end", "run-end", "approve", "sync"]),
185
+ data: z.unknown()
186
+ });
187
+ var TestBeginDataSchema = z.object({
188
+ id: z.string(),
189
+ title: z.string(),
190
+ titlePath: z.array(z.string()),
191
+ browser: z.string(),
192
+ location: LocationSchema
193
+ });
194
+ var TestEndDataSchema = z.object({
195
+ id: z.string(),
196
+ status: z.enum(["passed", "failed", "skipped"]),
197
+ attachments: z.array(AttachmentSchema),
198
+ error: z.string().optional(),
199
+ duration: z.number().optional()
200
+ });
201
+ var ReportDataSchema = z.object({
202
+ isRunning: z.boolean(),
203
+ tests: z.record(z.string(), TestDataSchema),
204
+ browsers: z.array(z.string()),
205
+ isUpdateMode: z.boolean(),
206
+ screenshotDir: z.string()
207
+ });
208
+ var LoadedReportDataSchema = z.object({
209
+ tests: z.record(z.string(), TestDataSchema).optional(),
210
+ isUpdateMode: z.boolean().optional()
211
+ });
212
+ var OfflineEventSchema = z.object({
213
+ type: z.enum(["test-begin", "test-end", "run-end"]),
214
+ data: z.unknown(),
215
+ timestamp: z.number(),
216
+ workerIndex: z.number()
217
+ });
218
+ var OfflineReportSchema = z.object({
219
+ version: z.number(),
220
+ generatedAt: z.string(),
221
+ workers: z.number(),
222
+ events: z.array(OfflineEventSchema)
223
+ });
224
+ var ApproveRequestBodySchema = z.object({
225
+ id: z.string(),
226
+ retry: z.number(),
227
+ image: z.string()
228
+ });
229
+ var ReportApiResponseSchema = z.object({
230
+ tests: z.record(z.string(), TestDataSchema),
231
+ isUpdateMode: z.boolean().optional()
232
+ });
233
+ var ClientBootstrapDataSchema = z.object({
234
+ report: ReportApiResponseSchema.extend({
235
+ isUpdateMode: z.boolean()
236
+ }),
237
+ liveUpdates: z.boolean(),
238
+ approvalEnabled: z.boolean(),
239
+ approvalMessage: z.string().optional()
240
+ });
241
+ var ImagesViewModeSchema = z.enum(["side-by-side", "swap", "slide", "blend"]);
242
+ function safeParse(schema, data) {
243
+ const result = schema.safeParse(data);
244
+ if (result.success) {
245
+ return result.data;
246
+ }
247
+ return null;
248
+ }
249
+
250
+ export {
251
+ createMutableReportState,
252
+ applyTestBeginEvent,
253
+ applyTestEndEvent,
254
+ finalizeRunEvent,
255
+ WebSocketMessageSchema,
256
+ TestBeginDataSchema,
257
+ TestEndDataSchema,
258
+ LoadedReportDataSchema,
259
+ OfflineReportSchema,
260
+ ApproveRequestBodySchema,
261
+ ClientBootstrapDataSchema,
262
+ safeParse
263
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { type ServerOptions } from './server.ts';
3
+ interface ResolvedCliOptions extends ServerOptions {
4
+ port: number;
5
+ screenshotDir: string;
6
+ reportPath: string;
7
+ }
8
+ export declare function resolveCliOptions(args: string[]): ResolvedCliOptions;
9
+ export {};
10
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAKA,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AAM7D,UAAU,kBAAmB,SAAQ,aAAa;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,kBAAkB,CA0BpE"}
package/dist/cli.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ startServer
4
+ } from "./chunk-BRFA7JCQ.js";
5
+ import "./chunk-IEYNAB6M.js";
6
+
7
+ // src/cli.ts
8
+ import { join } from "path";
9
+ import { parseArgs } from "util";
10
+
11
+ // src/is-direct-execution.ts
12
+ import { realpathSync } from "fs";
13
+ import { resolve } from "path";
14
+ import { fileURLToPath, pathToFileURL } from "url";
15
+ function resolveRealPath(pathValue) {
16
+ try {
17
+ return realpathSync.native(pathValue);
18
+ } catch {
19
+ return resolve(pathValue);
20
+ }
21
+ }
22
+ function isDirectExecution(moduleUrl) {
23
+ const entryPath = process.argv[1];
24
+ if (entryPath === void 0) {
25
+ return false;
26
+ }
27
+ return pathToFileURL(resolveRealPath(resolve(entryPath))).href === pathToFileURL(resolveRealPath(fileURLToPath(moduleUrl))).href;
28
+ }
29
+
30
+ // src/cli.ts
31
+ var DEFAULT_PORT = 3e3;
32
+ var DEFAULT_SCREENSHOT_DIR = "./screenshots";
33
+ var DEFAULT_REPORT_PATH = "./report.json";
34
+ function resolveCliOptions(args) {
35
+ const { values, positionals } = parseArgs({
36
+ args,
37
+ allowPositionals: true,
38
+ options: {
39
+ port: { type: "string", short: "p", default: `${DEFAULT_PORT}` },
40
+ "screenshot-dir": { type: "string", short: "s" },
41
+ "report-path": { type: "string", short: "r" }
42
+ }
43
+ });
44
+ if (positionals.length > 1) {
45
+ throw new TypeError(`Expected at most one artifact directory, received ${positionals.length}`);
46
+ }
47
+ const artifactDir = positionals[0];
48
+ const reportPath = values["report-path"] ?? (artifactDir === void 0 ? DEFAULT_REPORT_PATH : join(artifactDir, "report.json"));
49
+ const screenshotDir = values["screenshot-dir"] ?? (artifactDir === void 0 ? DEFAULT_SCREENSHOT_DIR : join(artifactDir, "screenshots"));
50
+ return {
51
+ port: parseInt(values.port ?? `${DEFAULT_PORT}`, 10),
52
+ screenshotDir,
53
+ reportPath
54
+ };
55
+ }
56
+ if (isDirectExecution(import.meta.url)) {
57
+ await startServer(resolveCliOptions(process.argv.slice(2)));
58
+ }
59
+ export {
60
+ resolveCliOptions
61
+ };