ccqa 1.57.0 → 1.57.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/dist/bin/ccqa.mjs +42 -56
- package/dist/hub-client/index.d.cts +2 -2
- package/dist/hub-client/index.d.mts +2 -2
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -8200,52 +8200,15 @@ function exportedNames(source) {
|
|
|
8200
8200
|
function reviewEmittedFiles(input) {
|
|
8201
8201
|
const locators = collectLocators(input.files);
|
|
8202
8202
|
const said = input.caseText.join("\n");
|
|
8203
|
-
return [
|
|
8204
|
-
...
|
|
8205
|
-
...
|
|
8206
|
-
...
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
...weakerTwin(file, source),
|
|
8213
|
-
...unreached(file, source, input)
|
|
8214
|
-
])
|
|
8215
|
-
];
|
|
8216
|
-
}
|
|
8217
|
-
/**
|
|
8218
|
-
* A path with a placeholder segment in it — `/orders/{orderId}`. Writing one
|
|
8219
|
-
* in prose is a statement about the address, not about the page's contents:
|
|
8220
|
-
* there is no other reason to name the part that changes per run.
|
|
8221
|
-
*/
|
|
8222
|
-
const PLACEHOLDER_PATH = /\/[A-Za-z0-9_\-]+(?:\/[A-Za-z0-9_\-]+)*\/\{[A-Za-z0-9_]+\}/;
|
|
8223
|
-
/**
|
|
8224
|
-
* A case that says where the run must end up, and a test that never looks at
|
|
8225
|
-
* the address.
|
|
8226
|
-
*
|
|
8227
|
-
* The row being on screen is not the same claim as being on the screen that
|
|
8228
|
-
* shows it — a product that rendered the row without navigating passes, and
|
|
8229
|
-
* that is the failure the expectation was written to catch. Scoped to
|
|
8230
|
-
* placeholder paths because those are unambiguous; a path mentioned as scenery
|
|
8231
|
-
* ("on /settings, the button is shown") is not a claim about the address.
|
|
8232
|
-
*
|
|
8233
|
-
* Calibrated against a small set of hand-written case definitions: a few
|
|
8234
|
-
* name a placeholder path in their expectations, most of those already
|
|
8235
|
-
* assert on the URL, and the one that does not is the defect this was
|
|
8236
|
-
* written for.
|
|
8237
|
-
*/
|
|
8238
|
-
function unassertedPath(input) {
|
|
8239
|
-
const source = input.files.get(input.testPath);
|
|
8240
|
-
if (source === void 0) return [];
|
|
8241
|
-
const stated = input.caseText.find((text) => PLACEHOLDER_PATH.test(text));
|
|
8242
|
-
if (stated === void 0 || /\btoHaveURL\b/.test(source)) return [];
|
|
8243
|
-
return [{
|
|
8244
|
-
file: input.testPath,
|
|
8245
|
-
line: 1,
|
|
8246
|
-
rule: "unasserted-path",
|
|
8247
|
-
message: `the case says where the run ends up — "${PLACEHOLDER_PATH.exec(stated)?.[0]}" — and nothing here looks at the address. What the screen shows can be right while the screen is wrong. Assert the URL where the case says it changes`
|
|
8248
|
-
}];
|
|
8203
|
+
return [...decidesNothing(input), ...[...input.files].flatMap(([file, source]) => [
|
|
8204
|
+
...toolIdentifiers(file, source),
|
|
8205
|
+
...containerOfPageText(file, source),
|
|
8206
|
+
...unjustifiedFirst(file, source),
|
|
8207
|
+
...describeEcho(file, source),
|
|
8208
|
+
...unaskedAssertions(file, source, locators, said),
|
|
8209
|
+
...weakerTwin(file, source),
|
|
8210
|
+
...unreached(file, source, input)
|
|
8211
|
+
])];
|
|
8249
8212
|
}
|
|
8250
8213
|
/** `readonly name =`, `name(...): Locator {`, and `export … name`. */
|
|
8251
8214
|
const DECLARED = /(?:readonly\s+([A-Za-z0-9_]+)\s*=|\b([A-Za-z0-9_]+)\([^)]*\)\s*:\s*Locator\b|export\s+(?:async\s+)?(?:const|let|function|class)\s+([A-Za-z0-9_]+))/g;
|
|
@@ -13914,6 +13877,7 @@ function parseTraceEvents(jsonl) {
|
|
|
13914
13877
|
const frames = [];
|
|
13915
13878
|
const steps = [];
|
|
13916
13879
|
const pendingByCallId = /* @__PURE__ */ new Map();
|
|
13880
|
+
const wallOffsets = /* @__PURE__ */ new Map();
|
|
13917
13881
|
let maxTimestamp = -Infinity;
|
|
13918
13882
|
for (const line of jsonl.split("\n")) {
|
|
13919
13883
|
if (line.trim() === "") continue;
|
|
@@ -13937,8 +13901,13 @@ function parseTraceEvents(jsonl) {
|
|
|
13937
13901
|
if (type === "screencast-frame") {
|
|
13938
13902
|
if (typeof event.sha1 === "string" && typeof event.timestamp === "number") frames.push({
|
|
13939
13903
|
sha1: event.sha1,
|
|
13940
|
-
timestamp: event.timestamp
|
|
13904
|
+
timestamp: event.timestamp,
|
|
13905
|
+
...typeof event.frameSwapWallTime === "number" ? { swapWallTime: event.frameSwapWallTime } : {}
|
|
13941
13906
|
});
|
|
13907
|
+
} else if (type === "context-options") {
|
|
13908
|
+
if (typeof event.wallTime === "number" && typeof event.monotonicTime === "number") {
|
|
13909
|
+
if (!wallOffsets.has(event.origin)) wallOffsets.set(event.origin, event.wallTime - event.monotonicTime);
|
|
13910
|
+
}
|
|
13942
13911
|
} else if (type === "action") {
|
|
13943
13912
|
if (typeof event.callId === "string" && typeof event.startTime === "number" && typeof event.endTime === "number") steps.push({
|
|
13944
13913
|
title: titleOf$1(event),
|
|
@@ -13969,10 +13938,16 @@ function parseTraceEvents(jsonl) {
|
|
|
13969
13938
|
...pending,
|
|
13970
13939
|
endTime: maxTimestamp
|
|
13971
13940
|
});
|
|
13972
|
-
|
|
13941
|
+
const offset = wallOffsets.get("testRunner") ?? wallOffsets.values().next().value;
|
|
13942
|
+
const painted = offset !== void 0 && frames.every((f) => f.swapWallTime !== void 0);
|
|
13943
|
+
const placed = frames.map(({ sha1, timestamp, swapWallTime }) => ({
|
|
13944
|
+
sha1,
|
|
13945
|
+
timestamp: painted ? swapWallTime - offset : timestamp
|
|
13946
|
+
}));
|
|
13947
|
+
placed.sort((a, b) => a.timestamp - b.timestamp);
|
|
13973
13948
|
steps.sort((a, b) => a.startTime - b.startTime);
|
|
13974
13949
|
return {
|
|
13975
|
-
frames,
|
|
13950
|
+
frames: placed,
|
|
13976
13951
|
steps
|
|
13977
13952
|
};
|
|
13978
13953
|
}
|
|
@@ -13992,13 +13967,22 @@ function titleOf$1(event) {
|
|
|
13992
13967
|
return "";
|
|
13993
13968
|
}
|
|
13994
13969
|
/**
|
|
13995
|
-
*
|
|
13996
|
-
*
|
|
13997
|
-
|
|
13998
|
-
|
|
13970
|
+
* One paint interval at 60fps. An assertion passes on the DOM, so a step can
|
|
13971
|
+
* end before the frame showing that DOM is painted — at most one interval later.
|
|
13972
|
+
*/
|
|
13973
|
+
const ONE_FRAME_MS = 17;
|
|
13974
|
+
/**
|
|
13975
|
+
* The frame showing the page as it was at `time`: the last one painted within
|
|
13976
|
+
* a frame interval of it, or — when the step began before any frame was
|
|
13977
|
+
* captured — the first frame after. Null when the trace holds no frames at
|
|
13978
|
+
* all. `frames` comes from {@link parseTraceEvents}, sorted by paint time.
|
|
13979
|
+
*
|
|
13980
|
+
* A step's start takes the same allowance as its end: it is the same boundary
|
|
13981
|
+
* as the previous step's end, and would otherwise show that step unfinished.
|
|
13999
13982
|
*/
|
|
14000
13983
|
function frameAt(frames, time) {
|
|
14001
|
-
|
|
13984
|
+
const painted = time + ONE_FRAME_MS;
|
|
13985
|
+
return frames.findLast((f) => f.timestamp <= painted) ?? frames[0] ?? null;
|
|
14002
13986
|
}
|
|
14003
13987
|
//#endregion
|
|
14004
13988
|
//#region src/targets/playwright/trace-capture.ts
|
|
@@ -27368,7 +27352,7 @@ const evidenceCommand = addLanguageOption(new Command("evidence").argument("<cas
|
|
|
27368
27352
|
anchors = await findSourceAnchors(sourceNeedles([...recording.actions, ...recording.cleanup ?? []]), roots);
|
|
27369
27353
|
}
|
|
27370
27354
|
const out = resolve(cwd, opts.out ?? join(testCase.ref.dir, "evidence.md"));
|
|
27371
|
-
|
|
27355
|
+
const markdown = renderEvidence({
|
|
27372
27356
|
testCase,
|
|
27373
27357
|
recording,
|
|
27374
27358
|
test: {
|
|
@@ -27381,7 +27365,9 @@ const evidenceCommand = addLanguageOption(new Command("evidence").argument("<cas
|
|
|
27381
27365
|
...resolved.targetConfig.allowExpectInCleanup ? {} : { cleanupUnchecked: testCase.cleanupExpectations },
|
|
27382
27366
|
...review ? { review } : {},
|
|
27383
27367
|
...anchors ? { anchors } : {}
|
|
27384
|
-
})
|
|
27368
|
+
});
|
|
27369
|
+
await mkdir(dirname(out), { recursive: true });
|
|
27370
|
+
await writeFile(out, scrubEnvValues(markdown, buildProseEnvScrubMap([], [])), "utf8");
|
|
27385
27371
|
process.stdout.write(`${relative(cwd, out) || out}\n`);
|
|
27386
27372
|
hint("paste it into the pull request, or attach it — ccqa writes the table, not the body");
|
|
27387
27373
|
}));
|
|
@@ -35,9 +35,9 @@ declare const RunSchema: z.ZodObject<{
|
|
|
35
35
|
running: "running";
|
|
36
36
|
}>;
|
|
37
37
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
38
|
+
record: "record";
|
|
38
39
|
run: "run";
|
|
39
40
|
drift: "drift";
|
|
40
|
-
record: "record";
|
|
41
41
|
}>>;
|
|
42
42
|
drift: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
43
43
|
specs: z.ZodNumber;
|
|
@@ -831,9 +831,9 @@ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
|
|
|
831
831
|
declare const RunReportDataSchema: z.ZodObject<{
|
|
832
832
|
schemaVersion: z.ZodLiteral<1>;
|
|
833
833
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
834
|
+
record: "record";
|
|
834
835
|
run: "run";
|
|
835
836
|
drift: "drift";
|
|
836
|
-
record: "record";
|
|
837
837
|
}>>;
|
|
838
838
|
createdAt: z.ZodString;
|
|
839
839
|
runId: z.ZodNullable<z.ZodString>;
|
|
@@ -35,9 +35,9 @@ declare const RunSchema: z.ZodObject<{
|
|
|
35
35
|
running: "running";
|
|
36
36
|
}>;
|
|
37
37
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
38
|
+
record: "record";
|
|
38
39
|
run: "run";
|
|
39
40
|
drift: "drift";
|
|
40
|
-
record: "record";
|
|
41
41
|
}>>;
|
|
42
42
|
drift: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
43
43
|
specs: z.ZodNumber;
|
|
@@ -831,9 +831,9 @@ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
|
|
|
831
831
|
declare const RunReportDataSchema: z.ZodObject<{
|
|
832
832
|
schemaVersion: z.ZodLiteral<1>;
|
|
833
833
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
834
|
+
record: "record";
|
|
834
835
|
run: "run";
|
|
835
836
|
drift: "drift";
|
|
836
|
-
record: "record";
|
|
837
837
|
}>>;
|
|
838
838
|
createdAt: z.ZodString;
|
|
839
839
|
runId: z.ZodNullable<z.ZodString>;
|
package/dist/package.json
CHANGED