@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.
- package/CHANGELOG.md +95 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/chunk-BRFA7JCQ.js +729 -0
- package/dist/chunk-IEYNAB6M.js +263 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/index.css +1335 -0
- package/dist/index.html +14 -0
- package/dist/index.js +21106 -0
- package/dist/is-direct-execution.d.ts +2 -0
- package/dist/is-direct-execution.d.ts.map +1 -0
- package/dist/offline-reports.d.ts +9 -0
- package/dist/offline-reports.d.ts.map +1 -0
- package/dist/report-artifact.d.ts +12 -0
- package/dist/report-artifact.d.ts.map +1 -0
- package/dist/report-state.d.ts +28 -0
- package/dist/report-state.d.ts.map +1 -0
- package/dist/report-utils.d.ts +5 -0
- package/dist/report-utils.d.ts.map +1 -0
- package/dist/reporter-utils.d.ts +3 -0
- package/dist/reporter-utils.d.ts.map +1 -0
- package/dist/reporter.d.ts +34 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +393 -0
- package/dist/schemas.d.ts +380 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/server/app.d.ts +16 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/bun-adapter.d.ts +3 -0
- package/dist/server/bun-adapter.d.ts.map +1 -0
- package/dist/server/file-utils.d.ts +7 -0
- package/dist/server/file-utils.d.ts.map +1 -0
- package/dist/server/handlers.d.ts +21 -0
- package/dist/server/handlers.d.ts.map +1 -0
- package/dist/server/node-adapter.d.ts +3 -0
- package/dist/server/node-adapter.d.ts.map +1 -0
- package/dist/server/routes.d.ts +16 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/utils.d.ts +3 -0
- package/dist/server/utils.d.ts.map +1 -0
- package/dist/server/ws.d.ts +4 -0
- package/dist/server/ws.d.ts.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +7 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- 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
|
+
};
|