supercov 0.0.6 → 0.0.8
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/README.md +13 -0
- package/dist/agentJson.d.ts +1 -1
- package/dist/agentJson.d.ts.map +1 -1
- package/dist/agentJson.js.map +1 -1
- package/dist/cli.js +49 -9
- package/dist/cli.js.map +1 -1
- package/dist/directInstrumenter.d.ts +1 -1
- package/dist/directInstrumenter.d.ts.map +1 -1
- package/dist/directInstrumenter.js +30 -6
- package/dist/directInstrumenter.js.map +1 -1
- package/dist/integrity.d.ts.map +1 -1
- package/dist/integrity.js +14 -0
- package/dist/integrity.js.map +1 -1
- package/dist/launchSupervisor.d.ts +8 -0
- package/dist/launchSupervisor.d.ts.map +1 -1
- package/dist/launchSupervisor.js +53 -2
- package/dist/launchSupervisor.js.map +1 -1
- package/dist/nodeTest.d.ts.map +1 -1
- package/dist/nodeTest.js +5 -1
- package/dist/nodeTest.js.map +1 -1
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +8 -18
- package/dist/playwright.js.map +1 -1
- package/dist/project.d.ts +1 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +4 -0
- package/dist/project.js.map +1 -1
- package/dist/query.d.ts +3 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +133 -12
- package/dist/query.js.map +1 -1
- package/dist/resolve-loader.d.mts.map +1 -1
- package/dist/resolve-loader.mjs +12 -0
- package/dist/resolve-loader.mjs.map +1 -1
- package/dist/runAnalysis.d.ts.map +1 -1
- package/dist/runAnalysis.js +52 -13
- package/dist/runAnalysis.js.map +1 -1
- package/dist/runnerEvidence.d.ts +1 -1
- package/dist/runnerEvidence.d.ts.map +1 -1
- package/dist/runnerEvidence.js +2 -2
- package/dist/runnerEvidence.js.map +1 -1
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +633 -534
- package/dist/runtime.js.map +1 -1
- package/dist/sourceDiscovery.d.ts.map +1 -1
- package/dist/sourceDiscovery.js +11 -1
- package/dist/sourceDiscovery.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/query.js
CHANGED
|
@@ -271,7 +271,7 @@ function obligationSatisfied(obligation, selected) {
|
|
|
271
271
|
return obligation.options.some((option) => option.every((test) => selected.has(test)));
|
|
272
272
|
}
|
|
273
273
|
/** Exact branch-and-bound solver; MC/DC obligations retain their witness-pair structure. */
|
|
274
|
-
export function minimumTestSet(report, target = 100, metric = "all") {
|
|
274
|
+
export function minimumTestSet(report, target = 100, metric = "all", maxStates = 5_000) {
|
|
275
275
|
const unattributed = report.tests.filter((test) => test.role === "background" &&
|
|
276
276
|
(test.hits.length > 0 || test.decisions.some((decision) => decision.vectors.length > 0)));
|
|
277
277
|
if (unattributed.length > 0) {
|
|
@@ -307,6 +307,18 @@ export function minimumTestSet(report, target = 100, metric = "all") {
|
|
|
307
307
|
const seen = new Set();
|
|
308
308
|
const search = (selected, skipped, skippedByMetric) => {
|
|
309
309
|
exploredStates += 1;
|
|
310
|
+
if (exploredStates > maxStates) {
|
|
311
|
+
throw new SupercovError("MINIMIZATION_COMPLEXITY_LIMIT", `Exact minimization exceeded its ${maxStates.toLocaleString()}-state safety budget. Narrow the test view with --kind or --runner, or request a different target.`, {
|
|
312
|
+
details: {
|
|
313
|
+
candidateTests: candidateTests.length,
|
|
314
|
+
obligations: obligations.length,
|
|
315
|
+
exploredStates,
|
|
316
|
+
maxStates,
|
|
317
|
+
target,
|
|
318
|
+
metric,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
}
|
|
310
322
|
if (selected.size >= best.size)
|
|
311
323
|
return;
|
|
312
324
|
const stateKey = `${[...selected].sort().join(",")}|${[...skipped].sort().join(",")}`;
|
|
@@ -361,6 +373,7 @@ function coverageCommand(runId, options, child) {
|
|
|
361
373
|
options.filter !== "all" ? `--filter ${options.filter}` : undefined,
|
|
362
374
|
options.kind ? `--kind ${shellQuote(options.kind)}` : undefined,
|
|
363
375
|
options.runner ? `--runner ${shellQuote(options.runner)}` : undefined,
|
|
376
|
+
options.metric !== "all" ? `--metric ${options.metric}` : undefined,
|
|
364
377
|
]
|
|
365
378
|
.filter(Boolean)
|
|
366
379
|
.join(" ");
|
|
@@ -480,6 +493,54 @@ function attribution(report, selected) {
|
|
|
480
493
|
serverFallback: phases.reduce((sum, phase) => sum + phase.inferredServerEvents, 0),
|
|
481
494
|
};
|
|
482
495
|
}
|
|
496
|
+
function coverageDiagnostics(report, selected) {
|
|
497
|
+
const observed = attribution(report, selected);
|
|
498
|
+
const diagnostics = [];
|
|
499
|
+
if ((report.transport?.corruptRecords ?? 0) > 0) {
|
|
500
|
+
diagnostics.push({
|
|
501
|
+
code: "CORRUPT_EVIDENCE_RECORDS",
|
|
502
|
+
severity: "error",
|
|
503
|
+
message: `${report.transport.corruptRecords} malformed evidence record(s) in ` +
|
|
504
|
+
`${report.transport.corruptFiles} file(s) were excluded; coverage is incomplete.`,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if ((report.transport?.remoteLaunches ?? 0) > 0 &&
|
|
508
|
+
(report.transport?.scopedServerRecords ?? 0) === 0 &&
|
|
509
|
+
observed.serverExplicit === 0 &&
|
|
510
|
+
observed.serverFallback === 0) {
|
|
511
|
+
diagnostics.push({
|
|
512
|
+
code: "REMOTE_SERVER_EVIDENCE_MISSING",
|
|
513
|
+
severity: "warning",
|
|
514
|
+
message: "Remote launches were supervised, but no server evidence returned. Coverage may describe only browser/test processes; inspect how the application server is launched.",
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return diagnostics;
|
|
518
|
+
}
|
|
519
|
+
function gapMetricValue(gap, metric) {
|
|
520
|
+
if (metric === "all")
|
|
521
|
+
return gap.score;
|
|
522
|
+
if (metric === "lines")
|
|
523
|
+
return gap.uncoveredLines;
|
|
524
|
+
if (metric === "statements")
|
|
525
|
+
return gap.uncoveredStatements;
|
|
526
|
+
if (metric === "functions")
|
|
527
|
+
return gap.uncoveredFunctions;
|
|
528
|
+
if (metric === "branches")
|
|
529
|
+
return gap.missingBranches;
|
|
530
|
+
return gap.missingMcdcConditions;
|
|
531
|
+
}
|
|
532
|
+
function obligationMatchesMetric(obligation, metric) {
|
|
533
|
+
if (metric === "all")
|
|
534
|
+
return true;
|
|
535
|
+
const kindByMetric = {
|
|
536
|
+
lines: "line",
|
|
537
|
+
statements: "statement",
|
|
538
|
+
functions: "function",
|
|
539
|
+
branches: "branch",
|
|
540
|
+
mcdc: "mcdc",
|
|
541
|
+
};
|
|
542
|
+
return obligation.kind === kindByMetric[metric];
|
|
543
|
+
}
|
|
483
544
|
export function fileGaps(report, selected) {
|
|
484
545
|
const files = new Map();
|
|
485
546
|
const get = (file) => {
|
|
@@ -595,6 +656,7 @@ function findFile(report, selector) {
|
|
|
595
656
|
}
|
|
596
657
|
export function coverageMeasurement(report) {
|
|
597
658
|
const limitations = report.limitations ?? [];
|
|
659
|
+
const evidenceCorruptions = report.transport?.corruptRecords ?? 0;
|
|
598
660
|
const byKind = {
|
|
599
661
|
"dynamic-code": 0,
|
|
600
662
|
"semantic-safety": 0,
|
|
@@ -603,11 +665,13 @@ export function coverageMeasurement(report) {
|
|
|
603
665
|
for (const limitation of limitations)
|
|
604
666
|
byKind[limitation.kind] += 1;
|
|
605
667
|
return {
|
|
606
|
-
complete: limitations.length === 0,
|
|
668
|
+
complete: limitations.length === 0 && evidenceCorruptions === 0,
|
|
607
669
|
limitations: limitations.length,
|
|
670
|
+
evidenceCorruptions,
|
|
608
671
|
// Every current limitation removes source from the measured denominator.
|
|
609
|
-
blocking: limitations.length,
|
|
610
|
-
files: new Set(limitations.map((limitation) => limitation.file)).size
|
|
672
|
+
blocking: limitations.length + evidenceCorruptions,
|
|
673
|
+
files: new Set(limitations.map((limitation) => limitation.file)).size +
|
|
674
|
+
(report.transport?.corruptFiles ?? 0),
|
|
611
675
|
byKind,
|
|
612
676
|
};
|
|
613
677
|
}
|
|
@@ -628,9 +692,9 @@ const helpText = `Agent-oriented local coverage queries:
|
|
|
628
692
|
supercov runs <run-id> coverage kinds [--json]
|
|
629
693
|
supercov runs <run-id> coverage runners [--json]
|
|
630
694
|
supercov runs <run-id> coverage scope [--limit N] [--offset N] [--json]
|
|
631
|
-
supercov runs <run-id> coverage files [--filter all|passed|failed] [--limit N] [--offset N] [--json]
|
|
632
|
-
supercov runs <run-id> coverage gaps [--filter all|passed|failed] [--kind e2e] [--limit N] [--offset N] [--json]
|
|
633
|
-
supercov runs <run-id> coverage file <source-file> [--kind e2e] [--limit N] [--offset N] [--json]
|
|
695
|
+
supercov runs <run-id> coverage files [--metric all|lines|statements|functions|branches|mcdc] [--filter all|passed|failed] [--limit N] [--offset N] [--json]
|
|
696
|
+
supercov runs <run-id> coverage gaps [--metric all|lines|statements|functions|branches|mcdc] [--filter all|passed|failed] [--kind e2e] [--limit N] [--offset N] [--json]
|
|
697
|
+
supercov runs <run-id> coverage file <source-file> [--metric all|lines|statements|functions|branches|mcdc] [--kind e2e] [--limit N] [--offset N] [--json]
|
|
634
698
|
supercov runs <run-id> coverage decision <id|source-file:line> [--kind e2e] [--json]
|
|
635
699
|
supercov runs <run-id> coverage covers <source-file:line> [--kind e2e] [--json]
|
|
636
700
|
supercov runs <run-id> coverage test <id|name-fragment> [--kind e2e] [--limit N] [--json]
|
|
@@ -857,6 +921,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
857
921
|
outcome,
|
|
858
922
|
selectedTests.filter((test) => test.role === "test" && test.outcome === outcome).length,
|
|
859
923
|
]));
|
|
924
|
+
const diagnostics = coverageDiagnostics(report, selectedTestSet);
|
|
860
925
|
const result = {
|
|
861
926
|
run: run.id,
|
|
862
927
|
filters: queryFilters(options),
|
|
@@ -875,6 +940,8 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
875
940
|
coverageByKind: report.coverageByKind,
|
|
876
941
|
coverageByRunner: report.coverageByRunner,
|
|
877
942
|
attribution: attribution(report, selectedTestSet),
|
|
943
|
+
transport: report.transport,
|
|
944
|
+
diagnostics,
|
|
878
945
|
...(!selectedTestSet
|
|
879
946
|
? {
|
|
880
947
|
confidence: {
|
|
@@ -907,7 +974,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
907
974
|
}
|
|
908
975
|
: undefined,
|
|
909
976
|
};
|
|
910
|
-
return output(result, options, `run ${run.id}${filterLabel(options) ? ` (${filterLabel(options)})` : ""}${run.metadata?.testExitCode !== 0 ? ` [INVALID: test exit ${run.metadata?.testExitCode ?? "unknown"}]` : ""}${report.integrity?.stale ? ` [STALE: ${(report.integrity.staleReasons ?? []).join(", ")}]` : ""}\nlines ${pct(summary.lines.percentage)} (${summary.lines.covered}/${summary.lines.total})\nbranches ${pct(summary.branches.percentage)} (${summary.branches.covered}/${summary.branches.total})\nMC/DC ${pct(summary.conditionCoveragePct)} (${summary.coveredConditions}/${summary.conditions})\nmeasurement: ${measurement.complete ? "complete" : `incomplete — ${measurement.blocking} blocking limitation(s) in ${measurement.files} file(s)`}${!selectedTestSet ? `\nconfidence: ${report.lines.filter((line) => line.confidence?.level === "asserted").length} asserted lines, ${report.lines.filter((line) => line.confidence?.level === "action").length} action-linked, ${report.lines.filter((line) => line.confidence?.level === "executed").length} execution-only; ${report.decisions.reduce((total, decision) => total + decision.conditions.filter((condition) => condition.assertionCovered).length, 0)} assertion-linked MC/DC conditions` : ""}\n${testCount} test(s)${setupCount ? ` + ${setupCount} setup scope(s)` : ""}; outcomes ${Object.entries(testOutcomes).filter(([, count]) => count > 0).map(([outcome, count]) => `${outcome}=${count}`).join(", ") || "none"}; ${gaps.length} file(s) have unresolved coverage or measurement gaps`);
|
|
977
|
+
return output(result, options, `run ${run.id}${filterLabel(options) ? ` (${filterLabel(options)})` : ""}${run.metadata?.testExitCode !== 0 ? ` [INVALID: test exit ${run.metadata?.testExitCode ?? "unknown"}]` : ""}${report.integrity?.stale ? ` [STALE: ${(report.integrity.staleReasons ?? []).join(", ")}]` : ""}\nlines ${pct(summary.lines.percentage)} (${summary.lines.covered}/${summary.lines.total})\nbranches ${pct(summary.branches.percentage)} (${summary.branches.covered}/${summary.branches.total})\nMC/DC ${pct(summary.conditionCoveragePct)} (${summary.coveredConditions}/${summary.conditions})\nmeasurement: ${measurement.complete ? "complete" : `incomplete — ${measurement.blocking} blocking limitation(s) in ${measurement.files} file(s)`}${diagnostics.length ? `\ndiagnostic: ${diagnostics.map((item) => `${item.code}: ${item.message}`).join("; ")}` : ""}${!selectedTestSet ? `\nconfidence: ${report.lines.filter((line) => line.confidence?.level === "asserted").length} asserted lines, ${report.lines.filter((line) => line.confidence?.level === "action").length} action-linked, ${report.lines.filter((line) => line.confidence?.level === "executed").length} execution-only; ${report.decisions.reduce((total, decision) => total + decision.conditions.filter((condition) => condition.assertionCovered).length, 0)} assertion-linked MC/DC conditions` : ""}\n${testCount} test(s)${setupCount ? ` + ${setupCount} setup scope(s)` : ""}; outcomes ${Object.entries(testOutcomes).filter(([, count]) => count > 0).map(([outcome, count]) => `${outcome}=${count}`).join(", ") || "none"}; ${gaps.length} file(s) have unresolved coverage or measurement gaps`);
|
|
911
978
|
}
|
|
912
979
|
if (command === "minimize") {
|
|
913
980
|
const solverReport = selectedTestSet
|
|
@@ -928,7 +995,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
928
995
|
};
|
|
929
996
|
});
|
|
930
997
|
const selectedPage = page(selectedDetails, options);
|
|
931
|
-
const base = `${coverageCommand(run.id, options, "minimize")} --target ${options.target}
|
|
998
|
+
const base = `${coverageCommand(run.id, options, "minimize")} --target ${options.target}`;
|
|
932
999
|
const next = nextPageCommand(base, selectedDetails.length, selectedPage.length, options);
|
|
933
1000
|
return output({
|
|
934
1001
|
run: run.id,
|
|
@@ -996,7 +1063,13 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
996
1063
|
}
|
|
997
1064
|
if (command === "files" || command === "gaps") {
|
|
998
1065
|
const files = fileGaps(report, selectedTestSet);
|
|
999
|
-
const all =
|
|
1066
|
+
const all = files
|
|
1067
|
+
.filter((gap) => command === "files" ||
|
|
1068
|
+
gapMetricValue(gap, options.metric) > 0 ||
|
|
1069
|
+
gap.measurementLimitations > 0)
|
|
1070
|
+
.sort((left, right) => gapMetricValue(right, options.metric) - gapMetricValue(left, options.metric) ||
|
|
1071
|
+
right.measurementLimitations - left.measurementLimitations ||
|
|
1072
|
+
left.file.localeCompare(right.file));
|
|
1000
1073
|
const selectedFiles = page(all, options);
|
|
1001
1074
|
const pageStart = all.length === 0 ? 0 : options.offset + 1;
|
|
1002
1075
|
const pageEnd = Math.min(options.offset + selectedFiles.length, all.length);
|
|
@@ -1007,6 +1080,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1007
1080
|
return output({
|
|
1008
1081
|
run: run.id,
|
|
1009
1082
|
filters: queryFilters(options),
|
|
1083
|
+
metric: options.metric,
|
|
1010
1084
|
[command]: selectedFiles,
|
|
1011
1085
|
}, options, selectedFiles
|
|
1012
1086
|
.map((gap) => {
|
|
@@ -1094,7 +1168,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1094
1168
|
...functions,
|
|
1095
1169
|
...branches,
|
|
1096
1170
|
...mcdc,
|
|
1097
|
-
].sort((left, right) => left.line - right.line || left.kind.localeCompare(right.kind));
|
|
1171
|
+
].filter((obligation) => obligationMatchesMetric(obligation, options.metric)).sort((left, right) => left.line - right.line || left.kind.localeCompare(right.kind));
|
|
1098
1172
|
const allFileLimitations = (report.limitations ?? [])
|
|
1099
1173
|
.filter((limitation) => limitation.file === file)
|
|
1100
1174
|
.map((limitation) => ({
|
|
@@ -1126,6 +1200,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1126
1200
|
run: run.id,
|
|
1127
1201
|
filters: queryFilters(options),
|
|
1128
1202
|
file,
|
|
1203
|
+
metric: options.metric,
|
|
1129
1204
|
counts: {
|
|
1130
1205
|
uncoveredLines: uncoveredLines.length,
|
|
1131
1206
|
uncoveredStatements: statements.length,
|
|
@@ -1185,9 +1260,15 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1185
1260
|
matches = matches.map((decision) => filterDecision(decision, selectedTestSet));
|
|
1186
1261
|
const totalDecisionEvidence = Math.max(0, ...matches.map((decision) => Math.max(decision.vectorObservations.length, decision.conditions.length, decision.tests.length)));
|
|
1187
1262
|
matches = matches.map((decision) => {
|
|
1263
|
+
const totals = {
|
|
1264
|
+
conditions: decision.conditions.length,
|
|
1265
|
+
vectorObservations: decision.vectorObservations.length,
|
|
1266
|
+
tests: decision.tests.length,
|
|
1267
|
+
};
|
|
1188
1268
|
const vectorObservations = page(decision.vectorObservations, options);
|
|
1189
1269
|
return {
|
|
1190
1270
|
...decision,
|
|
1271
|
+
totals,
|
|
1191
1272
|
vectors: vectorObservations.map((observation) => observation.vector),
|
|
1192
1273
|
vectorObservations,
|
|
1193
1274
|
conditions: page(decision.conditions, options),
|
|
@@ -1199,6 +1280,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1199
1280
|
const result = {
|
|
1200
1281
|
run: run.id,
|
|
1201
1282
|
filters: queryFilters(options),
|
|
1283
|
+
paginationAppliesTo: "conditions, vectorObservations, and tests independently within each decision",
|
|
1202
1284
|
decisions: matches,
|
|
1203
1285
|
};
|
|
1204
1286
|
return output(result, options, matches
|
|
@@ -1282,6 +1364,40 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1282
1364
|
}, options, `${matchingTests.map((test) => `${test.name} [${test.id}] — ${test.outcome}`).join("\n")}\n${pageLabel(matches.length, matchingTests.length, options)} matching tests${matchesNext ? `\nnext page: ${matchesNext}` : ""}`, queryPagination(matches.length, matchingTests.length, options));
|
|
1283
1365
|
}
|
|
1284
1366
|
const test = matches[0];
|
|
1367
|
+
const pointById = new Map(report.points.map((point) => [point.meta.id, point.meta]));
|
|
1368
|
+
const branchAlternativeById = new Map(report.branches.flatMap((branch) => branch.alternatives.map((alternative) => [
|
|
1369
|
+
alternative.id,
|
|
1370
|
+
{
|
|
1371
|
+
...branch.meta,
|
|
1372
|
+
id: alternative.id,
|
|
1373
|
+
alternative: alternative.label,
|
|
1374
|
+
},
|
|
1375
|
+
])));
|
|
1376
|
+
const allHitDetails = test.hits.map((id) => {
|
|
1377
|
+
const point = pointById.get(id);
|
|
1378
|
+
if (point)
|
|
1379
|
+
return {
|
|
1380
|
+
id: point.id,
|
|
1381
|
+
obligation: point.kind,
|
|
1382
|
+
file: point.file,
|
|
1383
|
+
line: point.line,
|
|
1384
|
+
column: point.column,
|
|
1385
|
+
label: point.label,
|
|
1386
|
+
};
|
|
1387
|
+
const branch = branchAlternativeById.get(id);
|
|
1388
|
+
if (branch)
|
|
1389
|
+
return {
|
|
1390
|
+
id: branch.id,
|
|
1391
|
+
obligation: "branch",
|
|
1392
|
+
branchKind: branch.kind,
|
|
1393
|
+
file: branch.file,
|
|
1394
|
+
line: branch.line,
|
|
1395
|
+
column: branch.column,
|
|
1396
|
+
alternative: branch.alternative,
|
|
1397
|
+
};
|
|
1398
|
+
return { id, obligation: "unknown" };
|
|
1399
|
+
});
|
|
1400
|
+
const decisionById = new Map(report.decisions.map((decision) => [decision.meta.id, decision.meta]));
|
|
1285
1401
|
const allPhases = report.phases
|
|
1286
1402
|
.filter((phase) => phase.test === test.id)
|
|
1287
1403
|
.map((phase) => ({
|
|
@@ -1299,7 +1415,11 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1299
1415
|
...test,
|
|
1300
1416
|
lines: page(test.lines, options),
|
|
1301
1417
|
hits: page(test.hits, options),
|
|
1302
|
-
|
|
1418
|
+
hitDetails: page(allHitDetails, options),
|
|
1419
|
+
decisions: page(test.decisions, options).map((decision) => ({
|
|
1420
|
+
...decision,
|
|
1421
|
+
meta: decisionById.get(decision.id),
|
|
1422
|
+
})),
|
|
1303
1423
|
phases: page(allPhases, options),
|
|
1304
1424
|
totals: {
|
|
1305
1425
|
lines: test.lines.length,
|
|
@@ -1313,6 +1433,7 @@ export async function runQueryCommand(command, args, root = process.cwd()) {
|
|
|
1313
1433
|
return output({
|
|
1314
1434
|
run: run.id,
|
|
1315
1435
|
filters: queryFilters(options),
|
|
1436
|
+
paginationAppliesTo: "lines, hits/hitDetails, decisions, and phases independently within the test",
|
|
1316
1437
|
tests: [selected],
|
|
1317
1438
|
}, options, `${selected.name}\noutcome ${selected.outcome}${selected.attempts.length ? `; ${selected.attempts.map((attempt) => `retry ${attempt.retry}=${attempt.status}`).join(", ")}` : ""}\n${selected.totals.lines} lines, ${selected.totals.hits} hits, ${selected.totals.decisions} decisions, ${selected.totals.phases} phases\n${selected.lines.map((line) => `line: ${line.file}:${line.line}`).join("\n")}${selected.lines.length && selected.phases.length ? "\n" : ""}${selected.phases.map((phase) => `${phase.kind}: ${phase.operation}${phase.source ? ` at ${phase.source}` : ""}`).join("\n")}\n${pageLabel(testTotal, testReturned, options)} per evidence category${testNext ? `\nnext page: ${testNext}` : ""}`, queryPagination(testTotal, testReturned, options));
|
|
1318
1439
|
}
|