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.
@@ -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.7.0",
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.7.0",
124
- "@supercov/cli-darwin-x64": "4.7.0",
125
- "@supercov/cli-linux-arm64-gnu": "4.7.0",
126
- "@supercov/cli-linux-arm64-musl": "4.7.0",
127
- "@supercov/cli-linux-x64-gnu": "4.7.0",
128
- "@supercov/cli-linux-x64-musl": "4.7.0",
129
- "@supercov/cli-win32-arm64": "4.7.0",
130
- "@supercov/cli-win32-x64": "4.7.0"
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
- return `${(_a8 = record.phaseId) != null ? _a8 : "unscoped"}:${record.type}:${suffix}`;
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
- const serialized = __spreadValues(__spreadValues({}, record), scope ? { scope } : {});
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")