supercov 4.7.0 → 4.8.1
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/docs/coverage-model.md +14 -1
- package/package.json +10 -10
- package/runtime/javascript/runtime.mjs +53 -2
package/docs/coverage-model.md
CHANGED
|
@@ -110,9 +110,22 @@ By test kind
|
|
|
110
110
|
directory has any, so a directory's `All` never stands above its kinds
|
|
111
111
|
unexplained.
|
|
112
112
|
|
|
113
|
+
A request to your app server that names no test -- a webhook from a mock
|
|
114
|
+
Stripe server Supercov does not measure -- is credited to the test running when
|
|
115
|
+
the server handled it, if that test was the only one running then. Where a
|
|
116
|
+
server serves one worker's tests, as a server started per worker does, only
|
|
117
|
+
that worker's tests count, so the request is credited while other workers run
|
|
118
|
+
theirs. A mock that runs under Supercov passes the test's identity on in its
|
|
119
|
+
own requests, and its webhook is credited exactly. The run's `Attribution`
|
|
120
|
+
line counts tests credited by time as `with server work credited by time`, and
|
|
121
|
+
each one's `runs <id> test` page says so. Work done while tests ran at once on
|
|
122
|
+
the same server, or while none ran, stays no test's.
|
|
123
|
+
|
|
113
124
|
A test that made assertions and has no covered line to its name is counted in
|
|
114
125
|
a warning on the summary. `npx supercov runs latest tests without-evidence`
|
|
115
|
-
lists every such test, with its file, kind and outcome.
|
|
126
|
+
lists every such test, with its file, kind and outcome. Where its file imports
|
|
127
|
+
no measured source, it says so: such a test can only have checked test code,
|
|
128
|
+
mocks or fixtures.
|
|
116
129
|
|
|
117
130
|
See [Supported suites](supported-suites.md) for the attribution available from
|
|
118
131
|
each runner.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.1",
|
|
4
4
|
"description": "Coverage, security and code quality for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coverage",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"eval:skill": "claude plugin eval plugins/supercov --scaffold --trust-plugin --no-publish --model claude-sonnet-5 --judge-model claude-haiku-4-5 --threshold 0.66 --tag behavior trigger --allow-tools Bash Edit Write --max-cost-usd 15",
|
|
84
84
|
"eval:skill-triggers": "claude plugin eval plugins/supercov --scaffold --trust-plugin --no-publish --model claude-sonnet-5 --judge-model claude-haiku-4-5 --threshold 0.66 --tag trigger --ablation none --max-cost-usd 5",
|
|
85
85
|
"test:engine": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace && npm run test:runtime && npm run test:rust-assets && npm run test:engine-integrations",
|
|
86
|
-
"test:engine-integrations": "cargo build -p supercov && node scripts/html-report-integration.mjs && node scripts/rust-process-supervision.mjs && node scripts/rust-direct-node-integration.mjs && node scripts/rust-public-run-integration.mjs && node scripts/rust-embedded-runtime-integration.mjs && node scripts/rust-direct-vitest-integration.mjs && node scripts/rust-direct-jest-integration.mjs && node scripts/mocha-failure-output.mjs && node scripts/failure-output.mjs node ava tap && node scripts/side-by-side.mjs node && node scripts/merge-across.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/rust-classic-browser-integration.mjs && node scripts/rust-top-packages-integration.mjs && node scripts/rust-generic-esbuild-integration.mjs && node scripts/rust-generic-tsc-integration.mjs && node scripts/rust-generic-build-matrix.mjs && node scripts/rust-vite-playwright-integration.mjs",
|
|
86
|
+
"test:engine-integrations": "cargo build -p supercov && node scripts/html-report-integration.mjs && node scripts/rust-process-supervision.mjs && node scripts/rust-direct-node-integration.mjs && node scripts/rust-public-run-integration.mjs && node scripts/rust-embedded-runtime-integration.mjs && node scripts/rust-direct-vitest-integration.mjs && node scripts/rust-direct-jest-integration.mjs && node scripts/mocha-failure-output.mjs && node scripts/failure-output.mjs node ava tap && node scripts/side-by-side.mjs node && node scripts/merge-across.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/background-by-time.mjs && node scripts/rust-classic-browser-integration.mjs && node scripts/rust-top-packages-integration.mjs && node scripts/rust-generic-esbuild-integration.mjs && node scripts/rust-generic-tsc-integration.mjs && node scripts/rust-generic-build-matrix.mjs && node scripts/rust-vite-playwright-integration.mjs",
|
|
87
87
|
"test:platform": "cargo test --workspace && cargo build -p supercov && node scripts/rust-process-supervision.mjs && node scripts/workspace-crash-integration.mjs",
|
|
88
88
|
"test:native-package": "cargo build --release -p supercov && node scripts/native-package-integration.mjs && node scripts/native-release-set-integration.mjs",
|
|
89
89
|
"test:pypi-wheel": "node scripts/pypi-wheel-integration.mjs",
|
|
@@ -120,14 +120,14 @@
|
|
|
120
120
|
"test:windows-tls": "node scripts/windows-tls-test.mjs"
|
|
121
121
|
},
|
|
122
122
|
"optionalDependencies": {
|
|
123
|
-
"@supercov/cli-darwin-arm64": "4.
|
|
124
|
-
"@supercov/cli-darwin-x64": "4.
|
|
125
|
-
"@supercov/cli-linux-arm64-gnu": "4.
|
|
126
|
-
"@supercov/cli-linux-arm64-musl": "4.
|
|
127
|
-
"@supercov/cli-linux-x64-gnu": "4.
|
|
128
|
-
"@supercov/cli-linux-x64-musl": "4.
|
|
129
|
-
"@supercov/cli-win32-arm64": "4.
|
|
130
|
-
"@supercov/cli-win32-x64": "4.
|
|
123
|
+
"@supercov/cli-darwin-arm64": "4.8.1",
|
|
124
|
+
"@supercov/cli-darwin-x64": "4.8.1",
|
|
125
|
+
"@supercov/cli-linux-arm64-gnu": "4.8.1",
|
|
126
|
+
"@supercov/cli-linux-arm64-musl": "4.8.1",
|
|
127
|
+
"@supercov/cli-linux-x64-gnu": "4.8.1",
|
|
128
|
+
"@supercov/cli-linux-x64-musl": "4.8.1",
|
|
129
|
+
"@supercov/cli-win32-arm64": "4.8.1",
|
|
130
|
+
"@supercov/cli-win32-x64": "4.8.1"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
133
|
"@playwright/test": ">=1.55.0",
|
|
@@ -461,7 +461,35 @@ function attemptKey(scope) {
|
|
|
461
461
|
function serverRecordKey(record) {
|
|
462
462
|
var _a8;
|
|
463
463
|
const suffix = record.type === "decision" ? `${record.meta.id}:${vectorKey(record.vector)}` : record.id;
|
|
464
|
-
|
|
464
|
+
// Each request that names no test keeps its own first hits: the report
|
|
465
|
+
// credits them to the test running when it came, and a later request's
|
|
466
|
+
// work belongs to whichever test was running then.
|
|
467
|
+
const request = record.request ? `request-${record.request}:` : "";
|
|
468
|
+
return `${request}${(_a8 = record.phaseId) != null ? _a8 : "unscoped"}:${record.type}:${suffix}`;
|
|
469
|
+
}
|
|
470
|
+
// Notes, once per test attempt, that this process served a request naming
|
|
471
|
+
// it. A request here that names no test is credited by time only among the
|
|
472
|
+
// attempts this process served: with a server per worker, the worker's test,
|
|
473
|
+
// whatever the other workers run meanwhile.
|
|
474
|
+
function noteServedAttempt(scope) {
|
|
475
|
+
if (!state.servedAttempts)
|
|
476
|
+
state.servedAttempts = /* @__PURE__ */ new Set();
|
|
477
|
+
const key = attemptKey(scope);
|
|
478
|
+
if (state.servedAttempts.has(key))
|
|
479
|
+
return;
|
|
480
|
+
state.servedAttempts.add(key);
|
|
481
|
+
const fs = getFs();
|
|
482
|
+
if (!fs)
|
|
483
|
+
return;
|
|
484
|
+
try {
|
|
485
|
+
appendDurableBackgroundRecord(fs, scope.runId, { type: "served", id: scope.attemptId, timestampMs: Date.now() });
|
|
486
|
+
} catch (e) {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Numbers the requests a server handles that name no test.
|
|
490
|
+
function nextUnscopedRequest() {
|
|
491
|
+
state.unscopedRequests = (state.unscopedRequests || 0) + 1;
|
|
492
|
+
return state.unscopedRequests;
|
|
465
493
|
}
|
|
466
494
|
function beginBufferedServerEvidence(scope) {
|
|
467
495
|
if (isBrowser)
|
|
@@ -794,7 +822,11 @@ function appendServer(record) {
|
|
|
794
822
|
try {
|
|
795
823
|
const directory = scope ? serverEvidenceDirectory(scope) : void 0;
|
|
796
824
|
const path = scope ? serverEvidencePath(scope) : void 0;
|
|
797
|
-
|
|
825
|
+
// Work done while handling a request that named no test -- a webhook a
|
|
826
|
+
// mock server sends -- says so, with the request's number.
|
|
827
|
+
const stored = serverPhaseStorage == null ? void 0 : serverPhaseStorage.getStore();
|
|
828
|
+
const unscopedRequest = !scope && stored && stored.request ? { request: stored.request } : {};
|
|
829
|
+
const serialized = __spreadValues(__spreadValues(__spreadValues({}, record), scope ? { scope } : {}), unscopedRequest);
|
|
798
830
|
if (scope && state.bufferedAttempts.has(attemptKey(scope))) {
|
|
799
831
|
const key = attemptKey(scope);
|
|
800
832
|
const buffered = (_c = state.serverBuffers.get(key)) != null ? _c : {
|
|
@@ -1387,9 +1419,26 @@ function installNodeRequestPropagation() {
|
|
|
1387
1419
|
// the server process were created by a shared before hook.
|
|
1388
1420
|
const incoming = requestCoverageContext(args[0]);
|
|
1389
1421
|
if (incoming) context = incoming.scope ? incoming : context != null ? context : currentRequestContext();
|
|
1422
|
+
if (incoming && incoming.scope)
|
|
1423
|
+
noteServedAttempt(incoming.scope);
|
|
1424
|
+
// A request that names no test, to a server no test owns, is numbered:
|
|
1425
|
+
// the report credits its work to the test running when it came.
|
|
1426
|
+
if (incoming && serverPhaseStorage && !(context && (context.scope || context.phaseId)))
|
|
1427
|
+
context = { request: nextUnscopedRequest() };
|
|
1428
|
+
// Node emits a request's body and its response's events outside the
|
|
1429
|
+
// handler's context, so a handler that reads the body -- `req.on('end')`
|
|
1430
|
+
// -- ran as no test's. The request and response carry it with them.
|
|
1431
|
+
if (incoming && context) {
|
|
1432
|
+
for (const emitter of args.slice(0, 2)) {
|
|
1433
|
+
if (emitter && typeof emitter === "object" && typeof emitter.emit === "function")
|
|
1434
|
+
emitterCoverageContexts.set(emitter, context);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1390
1437
|
}
|
|
1391
1438
|
const invoke = () => Reflect.apply(original, this, [event, ...args]);
|
|
1392
1439
|
try {
|
|
1440
|
+
if (context && context.request && !context.scope)
|
|
1441
|
+
return serverPhaseStorage.run(context, () => withProbeV2Context(context, invoke));
|
|
1393
1442
|
return context ? withCoverageCarrier({ version: 1, ...context }, invoke) : invoke();
|
|
1394
1443
|
} finally {
|
|
1395
1444
|
if (event === "close") emitterCoverageContexts.delete(this);
|
|
@@ -1410,6 +1459,8 @@ function withRequestPhase(handler, event) {
|
|
|
1410
1459
|
// owner. Shared servers have no captured test scope and stay unattributed.
|
|
1411
1460
|
const inheritedContext = requestContext && requestContext.scope ? {} : registeredContext.scope ? registeredContext : requestContext === void 0 ? currentRequestContext() : {};
|
|
1412
1461
|
const context = __spreadValues(__spreadValues({}, ((_a8 = requestContext == null ? void 0 : requestContext.scope) != null ? _a8 : inheritedContext.scope) ? { scope: (_b = requestContext == null ? void 0 : requestContext.scope) != null ? _b : inheritedContext.scope } : {}), ((_c = requestContext == null ? void 0 : requestContext.phaseId) != null ? _c : inheritedContext.phaseId) ? { phaseId: (_d = requestContext == null ? void 0 : requestContext.phaseId) != null ? _d : inheritedContext.phaseId } : {});
|
|
1462
|
+
if (requestContext !== void 0 && !context.scope)
|
|
1463
|
+
context.request = nextUnscopedRequest();
|
|
1413
1464
|
const invoke = () => {
|
|
1414
1465
|
if (event === "connection" && context.scope && args[0] &&
|
|
1415
1466
|
typeof args[0] === "object" && typeof args[0].emit === "function")
|