supercov 4.12.0 → 4.12.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/docs/cli.md +2 -2
- package/package.json +10 -10
- package/runtime/javascript/playwright.mjs +67 -16
- package/runtime/javascript/runtime.mjs +6 -0
package/docs/cli.md
CHANGED
|
@@ -699,7 +699,7 @@ rest of the run and leaves their old records out:
|
|
|
699
699
|
|
|
700
700
|
```sh supercov-example
|
|
701
701
|
npx supercov runs <suite-run> tests affected --names # after changing a mock
|
|
702
|
-
npx supercov --
|
|
702
|
+
npx supercov -- <those tests alone>
|
|
703
703
|
npx supercov merge
|
|
704
704
|
```
|
|
705
705
|
|
|
@@ -723,7 +723,7 @@ own:
|
|
|
723
723
|
|
|
724
724
|
```yaml
|
|
725
725
|
# Each of two jobs:
|
|
726
|
-
- run: npx supercov --
|
|
726
|
+
- run: npx supercov -- <test command for shard ${{ matrix.shard }}>
|
|
727
727
|
- uses: actions/upload-artifact@v4
|
|
728
728
|
with: { name: "supercov-${{ matrix.shard }}", path: .supercov/runs, include-hidden-files: true }
|
|
729
729
|
# Then one job:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "4.12.
|
|
3
|
+
"version": "4.12.2",
|
|
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/mock-reach.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/leaving-page.mjs && node scripts/background-by-time.mjs && node scripts/python-server-playwright.mjs && node scripts/ruby-server-playwright.mjs && node scripts/go-server-playwright.mjs && node scripts/jvm-server-playwright.mjs && node scripts/docker-server-playwright.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/mock-reach.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/leaving-page.mjs && node scripts/shared-page.mjs && node scripts/background-by-time.mjs && node scripts/python-server-playwright.mjs && node scripts/ruby-server-playwright.mjs && node scripts/go-server-playwright.mjs && node scripts/jvm-server-playwright.mjs && node scripts/docker-server-playwright.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.12.
|
|
124
|
-
"@supercov/cli-darwin-x64": "4.12.
|
|
125
|
-
"@supercov/cli-linux-arm64-gnu": "4.12.
|
|
126
|
-
"@supercov/cli-linux-arm64-musl": "4.12.
|
|
127
|
-
"@supercov/cli-linux-x64-gnu": "4.12.
|
|
128
|
-
"@supercov/cli-linux-x64-musl": "4.12.
|
|
129
|
-
"@supercov/cli-win32-arm64": "4.12.
|
|
130
|
-
"@supercov/cli-win32-x64": "4.12.
|
|
123
|
+
"@supercov/cli-darwin-arm64": "4.12.2",
|
|
124
|
+
"@supercov/cli-darwin-x64": "4.12.2",
|
|
125
|
+
"@supercov/cli-linux-arm64-gnu": "4.12.2",
|
|
126
|
+
"@supercov/cli-linux-arm64-musl": "4.12.2",
|
|
127
|
+
"@supercov/cli-linux-x64-gnu": "4.12.2",
|
|
128
|
+
"@supercov/cli-linux-x64-musl": "4.12.2",
|
|
129
|
+
"@supercov/cli-win32-arm64": "4.12.2",
|
|
130
|
+
"@supercov/cli-win32-x64": "4.12.2"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
133
|
"@playwright/test": ">=1.55.0",
|
|
@@ -391,6 +391,42 @@ class CoveragePhaseController {
|
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Start the pages an earlier test left open (a page a serial describe
|
|
396
|
+
* opens in `beforeAll`, a worker-scoped one) with no evidence, and return
|
|
397
|
+
* what they held. Their documents kept every hit since they loaded, and
|
|
398
|
+
* each later test was credited with what the tests before it ran. What
|
|
399
|
+
* they held is the earlier tests' (already recorded) and work between
|
|
400
|
+
* tests, such as a `beforeAll` signing in: no test's. A document that
|
|
401
|
+
* already has one of this test's phases is left alone: a reset that
|
|
402
|
+
* arrives late must not drop what this test ran.
|
|
403
|
+
*/
|
|
404
|
+
async takeCarriedEvidence() {
|
|
405
|
+
await Promise.all([...this.pendingRegistrations]);
|
|
406
|
+
const prefix = `${this.scope.attemptId}:`;
|
|
407
|
+
const take = (target) => bounded(target
|
|
408
|
+
.evaluate((prefix) => {
|
|
409
|
+
const coverageGlobal = globalThis;
|
|
410
|
+
if (String(coverageGlobal.__SUPERCOV_PHASE_ID__ ?? "").startsWith(prefix))
|
|
411
|
+
return undefined;
|
|
412
|
+
const getSnapshot = coverageGlobal.__SUPERCOV_COVERAGE_SNAPSHOT__;
|
|
413
|
+
const reset = coverageGlobal.__SUPERCOV_RESET__;
|
|
414
|
+
if (!getSnapshot || !reset)
|
|
415
|
+
return undefined;
|
|
416
|
+
const snapshot = JSON.stringify(getSnapshot());
|
|
417
|
+
reset();
|
|
418
|
+
return snapshot;
|
|
419
|
+
}, prefix)
|
|
420
|
+
.then((text) => (typeof text === "string" ? JSON.parse(text) : undefined))
|
|
421
|
+
.catch(() => undefined), WORKER_ACTIVATION_WAIT_MS);
|
|
422
|
+
const taken = await Promise.all(this.allPages().flatMap((page) => [
|
|
423
|
+
...page.frames().map(take),
|
|
424
|
+
...page.workers().map(take),
|
|
425
|
+
]));
|
|
426
|
+
const snapshots = taken.filter((snapshot) => snapshot && snapshot !== WORKER_TIMED_OUT &&
|
|
427
|
+
(snapshot.hits?.length || snapshot.events?.length || snapshot.decisions?.length));
|
|
428
|
+
return snapshots.length > 0 ? mergeSnapshots(snapshots) : undefined;
|
|
429
|
+
}
|
|
394
430
|
/**
|
|
395
431
|
* A page is registered once, and whoever asks while that is under way
|
|
396
432
|
* waits for it. Playwright announces a page to the context's listener
|
|
@@ -1458,6 +1494,25 @@ function mergeCollectedPhases(controllerPhases, fallbackPhases) {
|
|
|
1458
1494
|
});
|
|
1459
1495
|
return [...controllerPhases, ...remaining];
|
|
1460
1496
|
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Pool runners may cycle-restore a VM immediately after Playwright exits,
|
|
1499
|
+
* which can discard or overwrite the normal artifact copy. Each record is
|
|
1500
|
+
* written to one uniquely named, one-shot evidence file in the runner's shared
|
|
1501
|
+
* directory as well. No test streams into this path.
|
|
1502
|
+
*/
|
|
1503
|
+
function appendEvidenceLine(serialized) {
|
|
1504
|
+
const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
|
|
1505
|
+
(GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
|
|
1506
|
+
? undefined
|
|
1507
|
+
: GENERATED_EVIDENCE_DIRECTORY);
|
|
1508
|
+
if (!evidenceDirectory)
|
|
1509
|
+
return;
|
|
1510
|
+
const resolvedDirectory = resolve(process.cwd(), evidenceDirectory);
|
|
1511
|
+
const append = process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1"
|
|
1512
|
+
? appendJsonLineDurableSync
|
|
1513
|
+
: appendJsonLineSync;
|
|
1514
|
+
append(resolve(resolvedDirectory, `playwright-worker-${evidenceWriterIdentity()}-${process.pid}.mcdc.jsonl`), serialized);
|
|
1515
|
+
}
|
|
1461
1516
|
const instrumentedFixtures = {
|
|
1462
1517
|
page: async ({ page }, use, testInfo) => {
|
|
1463
1518
|
const scope = executionScope(testInfo);
|
|
@@ -1507,6 +1562,17 @@ const instrumentedFixtures = {
|
|
|
1507
1562
|
activeController = controller;
|
|
1508
1563
|
directRuntime()?.activateCoverageScope(scope);
|
|
1509
1564
|
await controller.adoptTrackedContexts().catch(() => undefined);
|
|
1565
|
+
const carried = await controller.takeCarriedEvidence().catch(() => undefined);
|
|
1566
|
+
if (carried) {
|
|
1567
|
+
appendEvidenceLine(`${JSON.stringify({
|
|
1568
|
+
testId: `__supercov_background__:playwright:${scope.attemptId}`,
|
|
1569
|
+
test: "Playwright work between tests",
|
|
1570
|
+
status: "unknown",
|
|
1571
|
+
provenance: { runner: "background", kind: "background", source: "explicit" },
|
|
1572
|
+
role: "background",
|
|
1573
|
+
browser: [carried],
|
|
1574
|
+
})}\n`);
|
|
1575
|
+
}
|
|
1510
1576
|
const serverOutput = serverEvidencePath(scope);
|
|
1511
1577
|
mkdirSync(serverEvidenceDirectory(scope), { recursive: true });
|
|
1512
1578
|
rmSync(serverOutput, { force: true });
|
|
@@ -1543,22 +1609,7 @@ const instrumentedFixtures = {
|
|
|
1543
1609
|
browser,
|
|
1544
1610
|
server,
|
|
1545
1611
|
};
|
|
1546
|
-
|
|
1547
|
-
// Pool runners may cycle-restore a VM immediately after Playwright
|
|
1548
|
-
// exits, which can discard or overwrite the normal artifact copy.
|
|
1549
|
-
// Write one uniquely named, one-shot evidence file to the runner's
|
|
1550
|
-
// shared directory as well. No test streams into this path.
|
|
1551
|
-
const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
|
|
1552
|
-
(GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
|
|
1553
|
-
? undefined
|
|
1554
|
-
: GENERATED_EVIDENCE_DIRECTORY);
|
|
1555
|
-
if (evidenceDirectory) {
|
|
1556
|
-
const resolvedDirectory = resolve(process.cwd(), evidenceDirectory);
|
|
1557
|
-
const append = process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1"
|
|
1558
|
-
? appendJsonLineDurableSync
|
|
1559
|
-
: appendJsonLineSync;
|
|
1560
|
-
append(resolve(resolvedDirectory, `playwright-worker-${evidenceWriterIdentity()}-${process.pid}.mcdc.jsonl`), serialized);
|
|
1561
|
-
}
|
|
1612
|
+
appendEvidenceLine(`${JSON.stringify(payload)}\n`);
|
|
1562
1613
|
}
|
|
1563
1614
|
finally {
|
|
1564
1615
|
await timed("controller.dispose", () => controller.dispose());
|
|
@@ -451,6 +451,12 @@ function persistBrowser() {
|
|
|
451
451
|
persistBrowserListenersInstalled = true;
|
|
452
452
|
try {
|
|
453
453
|
addEventListener("beforeunload", handOverBrowser);
|
|
454
|
+
// An iframe the page removes (a client-side navigation away from the
|
|
455
|
+
// page that held it, an embed closed) gets no `beforeunload`: what it ran
|
|
456
|
+
// went with it, though the test clicked in it. Its `pagehide` reaches the
|
|
457
|
+
// collector, unlike a top-level document's.
|
|
458
|
+
if (typeof window !== "undefined" && window !== window.top)
|
|
459
|
+
addEventListener("pagehide", handOverBrowser);
|
|
454
460
|
addEventListener("pagehide", persistBrowserNow);
|
|
455
461
|
addEventListener("visibilitychange", () => {
|
|
456
462
|
if (typeof document !== "undefined" && document.visibilityState === "hidden")
|