@crvy/rprtr 0.0.7 → 0.0.9
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 +10 -0
- package/dist/{chunk-OMDYTNWY.js → chunk-TS64TROX.js} +20 -139
- package/dist/cli.js +2 -2
- package/dist/debug-log.d.ts +3 -0
- package/dist/debug-log.d.ts.map +1 -0
- package/dist/index.css +0 -22
- package/dist/index.js +83 -127
- package/dist/reporter-artifact-ops.d.ts +22 -0
- package/dist/reporter-artifact-ops.d.ts.map +1 -0
- package/dist/reporter.cjs +194 -176
- package/dist/reporter.d.ts +0 -5
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +135 -117
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server.cjs +35 -154
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/server/report-watch.d.ts +0 -9
- package/dist/server/report-watch.d.ts.map +0 -1
- package/dist/{chunk-JGS2VWQP.js → chunk-EJRLZZ22.js} +142 -142
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ 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.0.9] - 2026-06-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Remove file watcher that interfered with live updates
|
|
13
|
+
## [0.0.8] - 2026-06-05
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Broadcast test-begin events to UI and allow running→terminal status transitions
|
|
8
18
|
## [0.0.7] - 2026-06-05
|
|
9
19
|
|
|
10
20
|
### Ci
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
finalizeRunEvent,
|
|
12
12
|
resolveBaselineTargets,
|
|
13
13
|
safeParse
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EJRLZZ22.js";
|
|
15
15
|
|
|
16
16
|
// src/server/app.ts
|
|
17
|
-
import { dirname as dirname3, join as
|
|
17
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
18
18
|
import { fileURLToPath } from "url";
|
|
19
19
|
import pLimit from "p-limit";
|
|
20
20
|
|
|
@@ -178,7 +178,9 @@ function broadcastToBrowsers(wsClients, msg) {
|
|
|
178
178
|
// src/server/handlers.ts
|
|
179
179
|
function handleTestBegin(ctx, data) {
|
|
180
180
|
const test = applyTestBeginEvent(ctx, data);
|
|
181
|
+
ctx.reportData.isRunning = true;
|
|
181
182
|
console.log(` \u25B6 [${test.browser ?? "?"}] ${test.title}`);
|
|
183
|
+
broadcastToBrowsers(ctx.wsClients, { type: "test-begin", data });
|
|
182
184
|
}
|
|
183
185
|
function handleTestEnd(ctx, data) {
|
|
184
186
|
const result = applyTestEndEvent(ctx, data);
|
|
@@ -208,123 +210,15 @@ function handleSync(ctx) {
|
|
|
208
210
|
broadcastToBrowsers(ctx.wsClients, { type: "sync", data: ctx.reportData });
|
|
209
211
|
}
|
|
210
212
|
|
|
211
|
-
// src/server/report-watch.ts
|
|
212
|
-
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
213
|
-
import { join as join2 } from "path";
|
|
214
|
-
var OFFLINE_REPORT_FILE_PATTERN2 = /^crvy-rprtr(?:-\d+)?\.json$/;
|
|
215
|
-
function createDebouncedRefresh(reload, delayMs = 50) {
|
|
216
|
-
let timer = null;
|
|
217
|
-
return () => {
|
|
218
|
-
if (timer !== null) {
|
|
219
|
-
clearTimeout(timer);
|
|
220
|
-
}
|
|
221
|
-
timer = setTimeout(() => {
|
|
222
|
-
timer = null;
|
|
223
|
-
void reload();
|
|
224
|
-
}, delayMs);
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
function isFileNotFound2(error) {
|
|
228
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
229
|
-
}
|
|
230
|
-
async function describeFile(filePath, label) {
|
|
231
|
-
try {
|
|
232
|
-
const fileStats = await stat2(filePath);
|
|
233
|
-
return `${label}:${fileStats.size}:${fileStats.mtimeMs}`;
|
|
234
|
-
} catch (error) {
|
|
235
|
-
if (isFileNotFound2(error)) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async function createOfflineFingerprint(offlineReportDir) {
|
|
242
|
-
if (!await isDirectory(offlineReportDir)) {
|
|
243
|
-
return "offline:missing";
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
const entries = await readdir2(offlineReportDir, { withFileTypes: true });
|
|
247
|
-
const relevantEntries = entries.filter(
|
|
248
|
-
(entry) => entry.isFile() && (entry.name === "report.json" || OFFLINE_REPORT_FILE_PATTERN2.test(entry.name))
|
|
249
|
-
).sort((left, right) => left.name.localeCompare(right.name));
|
|
250
|
-
const parts = await Promise.all(
|
|
251
|
-
relevantEntries.map((entry) => describeFile(join2(offlineReportDir, entry.name), entry.name))
|
|
252
|
-
);
|
|
253
|
-
return `offline:${parts.filter((part) => part !== null).join("|")}`;
|
|
254
|
-
} catch (error) {
|
|
255
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
256
|
-
console.warn(`[Server] Unable to scan ${offlineReportDir}: ${errorMsg}`);
|
|
257
|
-
return "offline:error";
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
async function createScreenshotFingerprint(screenshotDir, relativePath = "") {
|
|
261
|
-
const directoryPath = relativePath === "" ? screenshotDir : join2(screenshotDir, relativePath);
|
|
262
|
-
if (!await isDirectory(directoryPath)) {
|
|
263
|
-
return relativePath === "" ? ["screenshots:missing"] : [];
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
const entries = await readdir2(directoryPath, { withFileTypes: true });
|
|
267
|
-
const parts = await Promise.all(
|
|
268
|
-
entries.sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
269
|
-
const entryRelativePath = relativePath === "" ? entry.name : join2(relativePath, entry.name);
|
|
270
|
-
if (entry.isDirectory()) {
|
|
271
|
-
return createScreenshotFingerprint(screenshotDir, entryRelativePath);
|
|
272
|
-
}
|
|
273
|
-
return describeFile(join2(screenshotDir, entryRelativePath), entryRelativePath);
|
|
274
|
-
})
|
|
275
|
-
);
|
|
276
|
-
return parts.flat().filter((part) => part !== null);
|
|
277
|
-
} catch (error) {
|
|
278
|
-
if (isFileNotFound2(error)) {
|
|
279
|
-
return relativePath === "" ? ["screenshots:missing"] : [];
|
|
280
|
-
}
|
|
281
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
282
|
-
console.warn(`[Server] Unable to scan ${directoryPath}: ${errorMsg}`);
|
|
283
|
-
return relativePath === "" ? ["screenshots:error"] : [];
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
async function createArtifactsFingerprint(options) {
|
|
287
|
-
const [offlineFingerprint, screenshotFingerprint] = await Promise.all([
|
|
288
|
-
createOfflineFingerprint(options.offlineReportDir),
|
|
289
|
-
createScreenshotFingerprint(options.screenshotDir)
|
|
290
|
-
]);
|
|
291
|
-
return `${offlineFingerprint}::${screenshotFingerprint.join("|")}`;
|
|
292
|
-
}
|
|
293
|
-
async function watchReportArtifacts(options) {
|
|
294
|
-
let fingerprint = await createArtifactsFingerprint(options);
|
|
295
|
-
let isPolling = false;
|
|
296
|
-
const interval = setInterval(() => {
|
|
297
|
-
if (isPolling) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
isPolling = true;
|
|
301
|
-
void createArtifactsFingerprint(options).then((nextFingerprint) => {
|
|
302
|
-
if (nextFingerprint === fingerprint) {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
fingerprint = nextFingerprint;
|
|
306
|
-
options.scheduleRefresh();
|
|
307
|
-
}).catch((error) => {
|
|
308
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
309
|
-
console.warn(`[Server] Artifact refresh poll failed: ${errorMsg}`);
|
|
310
|
-
}).finally(() => {
|
|
311
|
-
isPolling = false;
|
|
312
|
-
});
|
|
313
|
-
}, 100);
|
|
314
|
-
return () => {
|
|
315
|
-
clearInterval(interval);
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
213
|
// src/server/routes.ts
|
|
320
214
|
import { existsSync } from "fs";
|
|
321
|
-
import { dirname as dirname2, join as
|
|
215
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
322
216
|
var LIVE_UPDATES_WEBSOCKET_PATH = "/";
|
|
323
217
|
function isWebSocketUpgradeRequest(req) {
|
|
324
218
|
return new URL(req.url).pathname === LIVE_UPDATES_WEBSOCKET_PATH && req.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
325
219
|
}
|
|
326
220
|
async function handleRoot(ctx) {
|
|
327
|
-
const html = await respondWithFile(
|
|
221
|
+
const html = await respondWithFile(join2(ctx.staticDir, "index.html"), "text/html");
|
|
328
222
|
return html ?? new Response("Not Found", { status: 404 });
|
|
329
223
|
}
|
|
330
224
|
async function handleAppCss() {
|
|
@@ -343,7 +237,7 @@ function handleApiReport(ctx) {
|
|
|
343
237
|
}
|
|
344
238
|
var APPROVAL_TARGET_ERROR = "Could not resolve approval target";
|
|
345
239
|
function actualPathFromUrl(ctx, actualUrl) {
|
|
346
|
-
return actualUrl.startsWith("/screenshots/") ?
|
|
240
|
+
return actualUrl.startsWith("/screenshots/") ? join2(ctx.reportData.screenshotDir, actualUrl.slice("/screenshots/".length)) : actualUrl;
|
|
347
241
|
}
|
|
348
242
|
function reporterTitlePath(test) {
|
|
349
243
|
const testFile = test.location?.file;
|
|
@@ -487,7 +381,7 @@ async function handleScreenshots(ctx, req) {
|
|
|
487
381
|
}
|
|
488
382
|
async function handleDist(ctx, req) {
|
|
489
383
|
const path = new URL(req.url).pathname.slice("/dist/".length);
|
|
490
|
-
const filePath =
|
|
384
|
+
const filePath = join2(ctx.staticDir, path);
|
|
491
385
|
const contentType = filePath.endsWith(".css") ? "text/css" : filePath.endsWith(".js") ? "application/javascript" : filePath.endsWith(".svelte") ? "text/plain" : "application/octet-stream";
|
|
492
386
|
const file = await respondWithFile(filePath, contentType);
|
|
493
387
|
return file ?? new Response("Not Found", { status: 404 });
|
|
@@ -590,10 +484,6 @@ async function loadOfflineReports(offlineReportDir, reportData) {
|
|
|
590
484
|
screenshotsBaseUrl: "/screenshots/"
|
|
591
485
|
});
|
|
592
486
|
}
|
|
593
|
-
function resetReloadableReportData(reportData) {
|
|
594
|
-
reportData.tests = {};
|
|
595
|
-
reportData.isUpdateMode = false;
|
|
596
|
-
}
|
|
597
487
|
async function handleParsedWebSocketMessage(ctx, msg) {
|
|
598
488
|
switch (msg.type) {
|
|
599
489
|
case "test-begin": {
|
|
@@ -645,16 +535,16 @@ async function resolveStaticDir(staticDir) {
|
|
|
645
535
|
const currentDir = dirname3(fileURLToPath(import.meta.url));
|
|
646
536
|
const candidates = staticDir === void 0 ? [
|
|
647
537
|
currentDir,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
] : [staticDir,
|
|
538
|
+
join3(currentDir, "dist"),
|
|
539
|
+
join3(currentDir, "..", "dist"),
|
|
540
|
+
join3(currentDir, "..", "..", "dist"),
|
|
541
|
+
join3(currentDir, ".."),
|
|
542
|
+
join3(currentDir, "..", "..")
|
|
543
|
+
] : [staticDir, join3(staticDir, "dist")];
|
|
654
544
|
const resolvedCandidates = await Promise.all(
|
|
655
545
|
candidates.map(async (candidate) => ({
|
|
656
546
|
candidate,
|
|
657
|
-
exists: await fileExists(
|
|
547
|
+
exists: await fileExists(join3(candidate, "index.html"))
|
|
658
548
|
}))
|
|
659
549
|
);
|
|
660
550
|
const resolved = resolvedCandidates.find(({ exists }) => exists);
|
|
@@ -665,7 +555,7 @@ async function resolveStaticDir(staticDir) {
|
|
|
665
555
|
}
|
|
666
556
|
async function resolveReportPath(reportPath) {
|
|
667
557
|
if (await isDirectory(reportPath)) {
|
|
668
|
-
return { reportFile:
|
|
558
|
+
return { reportFile: join3(reportPath, "report.json"), offlineReportDir: reportPath };
|
|
669
559
|
}
|
|
670
560
|
return { reportFile: reportPath, offlineReportDir: dirname3(reportPath) };
|
|
671
561
|
}
|
|
@@ -698,22 +588,13 @@ async function createServerApp(options = {}) {
|
|
|
698
588
|
const getHandlerContext = () => createHandlerContext(reportData, wsClients, currentRunIds, saveReport);
|
|
699
589
|
const handleRequest = (req) => handleHttpRequest(routesContext, req);
|
|
700
590
|
const handleWebSocketMessage = createWebSocketMessageHandler(getHandlerContext);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
await loadReport(reportFile, reportData);
|
|
704
|
-
await loadOfflineReports(offlineReportDir, reportData);
|
|
705
|
-
broadcastToBrowsers(wsClients, { type: "sync", data: reportData });
|
|
706
|
-
};
|
|
707
|
-
await reloadFromDisk();
|
|
708
|
-
const close = await watchReportArtifacts({
|
|
709
|
-
offlineReportDir,
|
|
710
|
-
screenshotDir: reportData.screenshotDir,
|
|
711
|
-
scheduleRefresh: createDebouncedRefresh(reloadFromDisk)
|
|
712
|
-
});
|
|
591
|
+
await loadReport(reportFile, reportData);
|
|
592
|
+
await loadOfflineReports(offlineReportDir, reportData);
|
|
713
593
|
return {
|
|
714
594
|
port,
|
|
715
595
|
wsClients,
|
|
716
|
-
close
|
|
596
|
+
close: () => {
|
|
597
|
+
},
|
|
717
598
|
handleRequest,
|
|
718
599
|
handleWebSocketMessage
|
|
719
600
|
};
|
package/dist/cli.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-log.d.ts","sourceRoot":"","sources":["../src/debug-log.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,OAAO,EAAE,KAAG,IAExC,CAAA;AACD,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,OAAO,EAAE,KAAG,IAE7C,CAAA"}
|
package/dist/index.css
CHANGED
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
--color-white: #fff;
|
|
25
25
|
--spacing: 0.25rem;
|
|
26
26
|
--container-xl: 36rem;
|
|
27
|
-
--container-2xl: 42rem;
|
|
28
27
|
--text-xs: 0.75rem;
|
|
29
28
|
--text-xs--line-height: calc(1 / 0.75);
|
|
30
29
|
--text-sm: 0.875rem;
|
|
@@ -391,9 +390,6 @@
|
|
|
391
390
|
.w-full {
|
|
392
391
|
width: 100%;
|
|
393
392
|
}
|
|
394
|
-
.max-w-2xl {
|
|
395
|
-
max-width: var(--container-2xl);
|
|
396
|
-
}
|
|
397
393
|
.max-w-full {
|
|
398
394
|
max-width: 100%;
|
|
399
395
|
}
|
|
@@ -541,12 +537,6 @@
|
|
|
541
537
|
.border-edge {
|
|
542
538
|
border-color: var(--color-edge);
|
|
543
539
|
}
|
|
544
|
-
.border-edge\/70 {
|
|
545
|
-
border-color: var(--color-edge);
|
|
546
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
547
|
-
border-color: color-mix(in oklab, var(--color-edge) 70%, transparent);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
540
|
.border-error {
|
|
551
541
|
border-color: var(--color-error);
|
|
552
542
|
}
|
|
@@ -559,12 +549,6 @@
|
|
|
559
549
|
border-color: color-mix(in oklab, var(--color-green-500) 60%, transparent);
|
|
560
550
|
}
|
|
561
551
|
}
|
|
562
|
-
.border-info\/40 {
|
|
563
|
-
border-color: var(--color-info);
|
|
564
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
565
|
-
border-color: color-mix(in oklab, var(--color-info) 40%, transparent);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
552
|
.border-purple-500\/60 {
|
|
569
553
|
border-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 60%, transparent);
|
|
570
554
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -619,12 +603,6 @@
|
|
|
619
603
|
.bg-info {
|
|
620
604
|
background-color: var(--color-info);
|
|
621
605
|
}
|
|
622
|
-
.bg-info\/10 {
|
|
623
|
-
background-color: var(--color-info);
|
|
624
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
625
|
-
background-color: color-mix(in oklab, var(--color-info) 10%, transparent);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
606
|
.bg-purple-500\/25 {
|
|
629
607
|
background-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 25%, transparent);
|
|
630
608
|
@supports (color: color-mix(in lab, red, red)) {
|