supercov 0.0.35 → 0.0.37
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 +29 -0
- package/docs/coverage-model.md +2 -1
- package/docs/getting-started.md +10 -3
- package/docs/supported-suites.md +33 -0
- package/docs/troubleshooting.md +8 -0
- package/docs/verification.md +6 -3
- package/package.json +12 -9
- package/runtime/javascript/playwright.mjs +45 -15
- package/runtime/javascript/resolve-loader.mjs +4 -2
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ npx supercov -- cargo test
|
|
|
40
40
|
npx supercov -- cargo nextest run
|
|
41
41
|
npx supercov -- pytest
|
|
42
42
|
npx supercov -- python -m unittest
|
|
43
|
+
npx supercov -- bundle exec rspec
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
## Give Supercov a job
|
|
@@ -101,6 +102,19 @@ From lines and branches to MC/DC, every gap becomes a concrete test target. Supe
|
|
|
101
102
|
|
|
102
103
|
The denominator comes from source structure before the run, so adding or removing tests cannot silently change what 100% means. Ambiguous source scope, uninstrumented code, and missing evidence remain visible as completeness blockers.
|
|
103
104
|
|
|
105
|
+
## Install for your language
|
|
106
|
+
|
|
107
|
+
The same binary ships through each language's own package manager, at the same version, from one release:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx supercov -- npm test # npm
|
|
111
|
+
uvx --from supercov-cli supercov -- pytest # PyPI
|
|
112
|
+
gem install supercov && supercov -- bundle exec rspec # RubyGems
|
|
113
|
+
cargo install supercov && supercov -- cargo test # crates.io, built from source
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`pip install supercov-cli` and `gem install supercov` install a wheel or gem that carries the binary for your platform; nothing is compiled. The crate builds from source and needs Rust 1.95.
|
|
117
|
+
|
|
104
118
|
## Supported languages
|
|
105
119
|
|
|
106
120
|
| Language | Status | Start with |
|
|
@@ -116,6 +130,18 @@ The denominator comes from source structure before the run, so adding or removin
|
|
|
116
130
|
|
|
117
131
|
Supercov requires Node.js 22 or newer. Rust support currently uses Rust 1.95; cargo-nextest 0.9.138 and 0.9.140 are supported. Python support requires CPython 3.12 or newer and measures pytest and unittest runs. Ruby support requires Ruby 3.3 or newer (3.4 or newer for full measurement) and measures RSpec, Minitest, test-unit and Cucumber runs.
|
|
118
132
|
|
|
133
|
+
## Supported operating systems and architectures
|
|
134
|
+
|
|
135
|
+
The CLI is a single native binary. `npx supercov` selects the build for the machine it runs on; nothing is compiled during installation.
|
|
136
|
+
|
|
137
|
+
| Operating system | Architectures | Notes |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| macOS | arm64 (Apple silicon), x64 (Intel) | macOS 11 or newer |
|
|
140
|
+
| Linux | arm64, x64 | glibc 2.28 or newer (Debian 10, Ubuntu 18.10, RHEL 8 and later), or musl (Alpine); chosen automatically |
|
|
141
|
+
| Windows | arm64, x64 | Windows 10 or newer |
|
|
142
|
+
|
|
143
|
+
On Windows, JavaScript and TypeScript suites are verified on every release; Python, Ruby, and Rust suites are not yet verified there. Containers, VMs, and remote executors run the same Linux builds. Every release publishes the same binaries to npm, PyPI (`supercov-cli`, a wheel per platform) and RubyGems (`supercov`, a gem per platform except arm64 Windows, which Ruby has no platform for), and the source to crates.io (`supercov`).
|
|
144
|
+
|
|
119
145
|
## Supported test suites
|
|
120
146
|
|
|
121
147
|
Supercov uses exact per-test attribution where an adapter is available. For other supported runners, it reports aggregate structural coverage instead of guessing which test covered a path.
|
|
@@ -129,6 +155,9 @@ Supercov uses exact per-test attribution where an adapter is available. For othe
|
|
|
129
155
|
| AVA and Mocha | Aggregate structural coverage |
|
|
130
156
|
| Cargo's standard libtest runner | Exact test and attempt identity |
|
|
131
157
|
| cargo-nextest | Exact test, attempt, retry, and binary identity |
|
|
158
|
+
| RSpec | Exact example and before/example/after phase identity |
|
|
159
|
+
| Minitest and test-unit | Exact test and setup/test/teardown identity |
|
|
160
|
+
| Cucumber | Exact scenario and hook-phase identity |
|
|
132
161
|
|
|
133
162
|
Supercov works with Vite, Next, Turbopack, Webpack, esbuild, SWC, and projects with no build step. One command can collect evidence from several supported runners into a single run.
|
|
134
163
|
|
package/docs/coverage-model.md
CHANGED
|
@@ -58,7 +58,8 @@ owners are allowed, and everyone else is denied. Branches, decision vectors, and
|
|
|
58
58
|
MC/DC expose those missing cases instead of treating one executed line as proof
|
|
59
59
|
that the decision is safe.
|
|
60
60
|
|
|
61
|
-
The same principle applies to Rust boolean expressions and
|
|
61
|
+
The same principle applies to Rust, Python, and Ruby boolean expressions and
|
|
62
|
+
control flow.
|
|
62
63
|
|
|
63
64
|
## What 100% means
|
|
64
65
|
|
package/docs/getting-started.md
CHANGED
|
@@ -15,15 +15,22 @@ Supercov supports JavaScript, TypeScript, Rust, Python, and Ruby today.
|
|
|
15
15
|
|
|
16
16
|
You need:
|
|
17
17
|
|
|
18
|
+
- macOS (arm64 or x64), Linux (arm64 or x64, glibc 2.28 or newer or musl), or
|
|
19
|
+
Windows (arm64 or x64; JavaScript and TypeScript suites are verified there
|
|
20
|
+
today);
|
|
18
21
|
- Node.js 22 or newer;
|
|
19
22
|
- a test command that already works in the repository; and
|
|
20
23
|
- for Rust, the Rust 1.95 toolchain;
|
|
21
24
|
- for Python, CPython 3.12 or newer with pytest or unittest;
|
|
22
25
|
- for Ruby, Ruby 3.4 or newer with RSpec, Minitest, test-unit or Cucumber (3.3 measures lines, methods and simple branches only).
|
|
23
26
|
|
|
24
|
-
The CLI is
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
The CLI is a native binary. `npx supercov` picks the build for your operating
|
|
28
|
+
system and architecture, and nothing is compiled on install; the same binary is
|
|
29
|
+
on PyPI as `supercov-cli` (`uvx --from supercov-cli supercov`) and on RubyGems
|
|
30
|
+
as `supercov` (`gem install supercov`), at the same version, and the source is
|
|
31
|
+
on crates.io (`cargo install supercov`). The first invocation may download
|
|
32
|
+
Supercov from the registry. Supercov itself does not upload your source or
|
|
33
|
+
coverage evidence to a Supercov service.
|
|
27
34
|
|
|
28
35
|
## 1. Run your real test command
|
|
29
36
|
|
package/docs/supported-suites.md
CHANGED
|
@@ -63,6 +63,20 @@ TypeScript, and TSX are supported.
|
|
|
63
63
|
Supercov instruments an isolated copy. It does not ask you to add an import,
|
|
64
64
|
reporter, plugin, or alternate build output.
|
|
65
65
|
|
|
66
|
+
A suite that runs its own compiled output is built before the tests start.
|
|
67
|
+
Supercov reads the tests: one that imports from `dist/`, or launches a package
|
|
68
|
+
script that does—`spawn("npm", ["run", "start"])`, `execSync("npm run
|
|
69
|
+
start")`, or any launch handed to the shell as one string—means the build is
|
|
70
|
+
part of the run, and a `build` script is run inside the isolated copy first.
|
|
71
|
+
Without it the server under test would never exist. A launch that only names a
|
|
72
|
+
build subcommand, such as `vite build`, is not taken as consuming a build.
|
|
73
|
+
|
|
74
|
+
Instrumented TypeScript is exempt from the host's type policy whether the
|
|
75
|
+
compile is Supercov's own build step or the test command's own `tsc`.
|
|
76
|
+
Instrumentation necessarily rewrites control-flow expressions in ways a type
|
|
77
|
+
checker cannot narrow through, so a project that compiles inside its test
|
|
78
|
+
command builds under measurement exactly as it does without it.
|
|
79
|
+
|
|
66
80
|
### Browsers, servers, and child processes
|
|
67
81
|
|
|
68
82
|
Playwright support includes Chromium, Firefox, and WebKit, along with pages,
|
|
@@ -82,6 +96,15 @@ a short drain window after the test command finishes so buffered evidence can
|
|
|
82
96
|
arrive. Work without a reliable test identity is kept as background coverage
|
|
83
97
|
instead of being assigned to an arbitrary test.
|
|
84
98
|
|
|
99
|
+
A child a test stops in teardown keeps the coverage it produced. Buffered
|
|
100
|
+
evidence is written when a terminating signal arrives—`SIGTERM`, `SIGINT`,
|
|
101
|
+
`SIGHUP`—not only when the process exits on its own, so killing a gateway
|
|
102
|
+
after the request it served does not lose the request. The program's own
|
|
103
|
+
signal handling is untouched: a process with no handler still dies from the
|
|
104
|
+
signal exactly as it would unmeasured, and one with its own handler keeps
|
|
105
|
+
it. `SIGKILL` cannot be caught by anything and is the one stop that loses
|
|
106
|
+
whatever was still buffered.
|
|
107
|
+
|
|
85
108
|
## Rust
|
|
86
109
|
|
|
87
110
|
| Runner | Attribution | Current requirement |
|
|
@@ -208,6 +231,16 @@ decisions, `||=`, loops, `rescue` flow, a second statement on a line) is
|
|
|
208
231
|
declared unmeasured on that interpreter rather than shown as a gap. Ruby 3.4
|
|
209
232
|
and newer measure everything.
|
|
210
233
|
|
|
234
|
+
Ruby reads its own coverage as the interpreter exits, and that shapes what a
|
|
235
|
+
stopped process keeps. A process ended by a signal it can catch—`SIGTERM`
|
|
236
|
+
from a test's teardown, `SIGINT`—unwinds through that exit and reports
|
|
237
|
+
everything it measured. A process killed with `SIGKILL`, or one that leaves
|
|
238
|
+
through `exit!`, never gets there and takes with it whatever it observed since
|
|
239
|
+
its last test boundary. Supercov cannot recover that or say which lines it
|
|
240
|
+
would have been, so the run declares that a process did not report, which
|
|
241
|
+
blocks completeness, rather than counting those lines against the code. Stop a
|
|
242
|
+
Ruby server with `SIGTERM`, or wait for it to exit, and it reports.
|
|
243
|
+
|
|
211
244
|
```sh
|
|
212
245
|
npx supercov -- rspec
|
|
213
246
|
npx supercov -- bundle exec rspec
|
package/docs/troubleshooting.md
CHANGED
|
@@ -100,6 +100,14 @@ Read the reason in the summary, `scope`, or `gaps` output. Fix a configuration
|
|
|
100
100
|
problem when one is named. Otherwise stop the coverage loop and report the
|
|
101
101
|
limit; do not change application code or add a meaningless test to chase 100%.
|
|
102
102
|
|
|
103
|
+
One limit names a cause you can remove. `ruby-process-did-not-report` means a
|
|
104
|
+
Ruby process the run measured ended without reporting: it was killed with
|
|
105
|
+
`SIGKILL`, or left through `exit!`, before the exit where Ruby hands over its
|
|
106
|
+
coverage. Whatever that process observed since its last test boundary is gone,
|
|
107
|
+
and its lines are not shown as uncovered because nothing can say whether they
|
|
108
|
+
ran. Stop the process with `SIGTERM` instead, or wait for it to exit, and the
|
|
109
|
+
limit does not appear.
|
|
110
|
+
|
|
103
111
|
## Coverage is aggregate instead of per test
|
|
104
112
|
|
|
105
113
|
Aggregate coverage still shows which source ran, but Supercov cannot truthfully
|
package/docs/verification.md
CHANGED
|
@@ -68,12 +68,15 @@ aggregate instead of being assigned to whichever test happened to be nearby.
|
|
|
68
68
|
|
|
69
69
|
Release checks cover program behavior before and after instrumentation, line and
|
|
70
70
|
branch results, decision vectors and MC/DC, supported JavaScript, TypeScript,
|
|
71
|
-
and
|
|
72
|
-
installation, interrupted-run recovery, and isolated
|
|
71
|
+
Rust, Python, and Ruby runner contracts, Chromium, Firefox, and WebKit
|
|
72
|
+
execution, clean installation, interrupted-run recovery, and isolated
|
|
73
|
+
publication.
|
|
73
74
|
|
|
74
75
|
JavaScript behavior is exercised against a pinned TC39 Test262 corpus. MC/DC
|
|
75
76
|
cases are also compared with an independent LLVM implementation so a
|
|
76
|
-
self-consistent calculation error does not pass unnoticed.
|
|
77
|
+
self-consistent calculation error does not pass unnoticed. Python and Ruby
|
|
78
|
+
gates run real suites through their supported runners and assert the resulting
|
|
79
|
+
coverage totals, test identity, and measurement limits.
|
|
77
80
|
|
|
78
81
|
These checks reduce risk; they do not replace reviewing the assertions and
|
|
79
82
|
behavior protected by a new test.
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Zero-edit, runner-aware coverage completeness for JavaScript, TypeScript, Rust, and
|
|
3
|
+
"version": "0.0.37",
|
|
4
|
+
"description": "Zero-edit, runner-aware coverage completeness for JavaScript, TypeScript, Rust, Python, and Ruby test suites",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/supercorp-ai/supercov.git"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://
|
|
10
|
+
"homepage": "https://supercov.com",
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/supercorp-ai/supercov/issues"
|
|
13
13
|
},
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"benchmark:python-monitoring": "cargo build -p supercov && node scripts/python-monitoring-benchmark.mjs",
|
|
66
66
|
"check": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && npm run test && npm run test:runtime && npm run test:rust-assets && node scripts/package-preflight.mjs",
|
|
67
67
|
"release:check": "npm run check && npm run test:engine && npm run test:fixture && npm run test:watchdog && npm run test:engine-contract && npm run test:agent && npm run test:child-attribution && npm run test:host-loader && npm run test:python-monitoring && npm run test:ruby-coverage && npm run test:packed-npx && npm run test:clang-mcdc && npm run benchmark:check",
|
|
68
|
+
"release:bump": "node scripts/bump-version.mjs",
|
|
68
69
|
"prepack": "node scripts/package-preflight.mjs",
|
|
69
70
|
"prepublishOnly": "npm run release:check",
|
|
70
71
|
"test:child-attribution": "cargo build -p supercov && node scripts/rust-child-attribution-integration.mjs",
|
|
@@ -73,12 +74,14 @@
|
|
|
73
74
|
"test:ruby-coverage": "cargo build -p supercov && node scripts/ruby-coverage-integration.mjs"
|
|
74
75
|
},
|
|
75
76
|
"optionalDependencies": {
|
|
76
|
-
"@supercov/cli-darwin-arm64": "0.0.
|
|
77
|
-
"@supercov/cli-darwin-x64": "0.0.
|
|
78
|
-
"@supercov/cli-linux-arm64-gnu": "0.0.
|
|
79
|
-
"@supercov/cli-linux-arm64-musl": "0.0.
|
|
80
|
-
"@supercov/cli-linux-x64-gnu": "0.0.
|
|
81
|
-
"@supercov/cli-linux-x64-musl": "0.0.
|
|
77
|
+
"@supercov/cli-darwin-arm64": "0.0.37",
|
|
78
|
+
"@supercov/cli-darwin-x64": "0.0.37",
|
|
79
|
+
"@supercov/cli-linux-arm64-gnu": "0.0.37",
|
|
80
|
+
"@supercov/cli-linux-arm64-musl": "0.0.37",
|
|
81
|
+
"@supercov/cli-linux-x64-gnu": "0.0.37",
|
|
82
|
+
"@supercov/cli-linux-x64-musl": "0.0.37",
|
|
83
|
+
"@supercov/cli-win32-arm64": "0.0.37",
|
|
84
|
+
"@supercov/cli-win32-x64": "0.0.37"
|
|
82
85
|
},
|
|
83
86
|
"peerDependencies": {
|
|
84
87
|
"@playwright/test": ">=1.55.0",
|
|
@@ -13,6 +13,7 @@ import http from "node:http";
|
|
|
13
13
|
import https from "node:https";
|
|
14
14
|
import { syncBuiltinESMExports } from "node:module";
|
|
15
15
|
import { relative, resolve, sep } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
16
17
|
import * as standardPlaywright from "@playwright/test";
|
|
17
18
|
import * as coverageRuntime from "./runtime.mjs";
|
|
18
19
|
import { inferTestProvenance } from "./provenance.mjs";
|
|
@@ -147,8 +148,11 @@ function callerSource() {
|
|
|
147
148
|
.replace(/\/$/, "");
|
|
148
149
|
if (!projectRoot)
|
|
149
150
|
return normalized;
|
|
151
|
+
// A stack frame's URL is what Node's pathToFileURL produces -- on Windows
|
|
152
|
+
// `file:///C:/...`, never a hand-built `file://C:/...` -- so strip the
|
|
153
|
+
// prefix derived the same way, then the bare path for frames without one.
|
|
150
154
|
return normalized
|
|
151
|
-
.replace(
|
|
155
|
+
.replace(`${pathToFileURL(projectRoot).href}/`, "")
|
|
152
156
|
.replace(`${projectRoot}/`, "");
|
|
153
157
|
}
|
|
154
158
|
class CoveragePhaseController {
|
|
@@ -168,9 +172,11 @@ class CoveragePhaseController {
|
|
|
168
172
|
scriptUpdate = Promise.resolve();
|
|
169
173
|
proxyCache = new WeakMap();
|
|
170
174
|
runtimeSnapshots;
|
|
171
|
-
// Contexts
|
|
175
|
+
// Contexts whose creation the collector did not observe: it does not know
|
|
172
176
|
// what `extraHTTPHeaders` their owner configured, so it must not rewrite
|
|
173
177
|
// them (the scope cookie and the fetch patch still carry attribution).
|
|
178
|
+
// A worker-scoped context launched through a patched BrowserType is found
|
|
179
|
+
// later but is not opaque: its original headers were captured at launch.
|
|
174
180
|
adoptedContexts = new Set();
|
|
175
181
|
// Listeners installed on contexts that outlive this test, removed on
|
|
176
182
|
// dispose so a worker-scoped context does not keep registering pages with
|
|
@@ -257,8 +263,10 @@ class CoveragePhaseController {
|
|
|
257
263
|
if (this.disposed)
|
|
258
264
|
return;
|
|
259
265
|
for (const context of liveTrackedContexts()) {
|
|
260
|
-
if (!this.contexts.has(context))
|
|
261
|
-
|
|
266
|
+
if (!this.contexts.has(context)) {
|
|
267
|
+
const headersKnown = trackedContextConfiguredHeaders.has(context);
|
|
268
|
+
await this.registerContext(context, trackedContextConfiguredHeaders.get(context) ?? this.configuredHeaders, { adopted: !headersKnown }).catch(() => undefined);
|
|
269
|
+
}
|
|
262
270
|
}
|
|
263
271
|
}
|
|
264
272
|
async registerPage(page) {
|
|
@@ -266,8 +274,11 @@ class CoveragePhaseController {
|
|
|
266
274
|
return;
|
|
267
275
|
timingCount("registerPage");
|
|
268
276
|
this.pages.add(page);
|
|
269
|
-
|
|
270
|
-
|
|
277
|
+
const context = page.context();
|
|
278
|
+
const tracked = liveTrackedContexts().has(context);
|
|
279
|
+
const headersKnown = trackedContextConfiguredHeaders.has(context);
|
|
280
|
+
await this.registerContext(context, trackedContextConfiguredHeaders.get(context) ?? this.configuredHeaders, {
|
|
281
|
+
adopted: !this.contexts.has(context) && tracked && !headersKnown,
|
|
271
282
|
});
|
|
272
283
|
const cdp = await page.context().newCDPSession(page).catch(() => undefined);
|
|
273
284
|
if (cdp)
|
|
@@ -399,6 +410,14 @@ class CoveragePhaseController {
|
|
|
399
410
|
this.disposed = true;
|
|
400
411
|
await Promise.all([...this.pendingRegistrations]);
|
|
401
412
|
await this.scriptUpdate;
|
|
413
|
+
// A worker-scoped context survives this test. Remove its test scope
|
|
414
|
+
// while preserving the exact headers supplied by the suite so work
|
|
415
|
+
// between tests cannot be charged to the attempt that just ended.
|
|
416
|
+
await Promise.all([...this.contexts]
|
|
417
|
+
.filter((context) => !this.adoptedContexts.has(context))
|
|
418
|
+
.map((context) => context
|
|
419
|
+
.setExtraHTTPHeaders(this.contextConfiguredHeaders.get(context) ?? {})
|
|
420
|
+
.catch(() => undefined)));
|
|
402
421
|
for (const [context, listeners] of this.contextListeners) {
|
|
403
422
|
for (const [event, listener] of listeners)
|
|
404
423
|
context.off?.(event, listener);
|
|
@@ -505,9 +524,10 @@ class CoveragePhaseController {
|
|
|
505
524
|
trackBrowser(result);
|
|
506
525
|
if (typeof candidate["pages"] === "function" &&
|
|
507
526
|
typeof candidate["route"] === "function") {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
527
|
+
const configuredHeaders = sourceArgs?.[0]
|
|
528
|
+
?.extraHTTPHeaders ?? this.configuredHeaders;
|
|
529
|
+
trackContext(result, configuredHeaders);
|
|
530
|
+
await this.registerContext(result, configuredHeaders);
|
|
511
531
|
}
|
|
512
532
|
if (typeof candidate["frames"] === "function" &&
|
|
513
533
|
typeof candidate["context"] === "function")
|
|
@@ -630,6 +650,10 @@ const controllers = new Map();
|
|
|
630
650
|
// launches are recorded here, and each test's controller adopts what is live.
|
|
631
651
|
const trackedBrowsers = new Set();
|
|
632
652
|
const trackedContexts = new Set();
|
|
653
|
+
// Creation-time headers for contexts launched through patched Playwright
|
|
654
|
+
// methods. Capturing an explicit empty object matters: it proves replacing
|
|
655
|
+
// the headers later is safe, unlike a context discovered only by enumeration.
|
|
656
|
+
const trackedContextConfiguredHeaders = new WeakMap();
|
|
633
657
|
const patchedPrototypes = new WeakSet();
|
|
634
658
|
function trackBrowser(browser) {
|
|
635
659
|
if (!browser || typeof browser !== "object" || trackedBrowsers.has(browser))
|
|
@@ -638,8 +662,12 @@ function trackBrowser(browser) {
|
|
|
638
662
|
browser.once?.("disconnected", () => trackedBrowsers.delete(browser));
|
|
639
663
|
patchBrowserPrototype(browser);
|
|
640
664
|
}
|
|
641
|
-
function trackContext(context) {
|
|
642
|
-
if (!context || typeof context !== "object"
|
|
665
|
+
function trackContext(context, configuredHeaders) {
|
|
666
|
+
if (!context || typeof context !== "object")
|
|
667
|
+
return;
|
|
668
|
+
if (configuredHeaders !== undefined)
|
|
669
|
+
trackedContextConfiguredHeaders.set(context, configuredHeaders);
|
|
670
|
+
if (trackedContexts.has(context))
|
|
643
671
|
return;
|
|
644
672
|
trackedContexts.add(context);
|
|
645
673
|
context.once?.("close", () => trackedContexts.delete(context));
|
|
@@ -685,10 +713,11 @@ function patchBrowserPrototype(browser) {
|
|
|
685
713
|
patchedPrototypes.add(prototype);
|
|
686
714
|
patchOnce(browser, "newContext", (original) => async function (...args) {
|
|
687
715
|
const context = await original.apply(this, args);
|
|
688
|
-
|
|
716
|
+
const configuredHeaders = args[0]?.extraHTTPHeaders ?? {};
|
|
717
|
+
trackContext(context, configuredHeaders);
|
|
689
718
|
const controller = activeController;
|
|
690
719
|
if (controller && !controller.contexts.has(context))
|
|
691
|
-
await controller.registerContext(context,
|
|
720
|
+
await controller.registerContext(context, configuredHeaders).catch(() => undefined);
|
|
692
721
|
return context;
|
|
693
722
|
});
|
|
694
723
|
}
|
|
@@ -734,10 +763,11 @@ function installBrowserLaunchTracking() {
|
|
|
734
763
|
}
|
|
735
764
|
patchOnce(browserType, "launchPersistentContext", (original) => async function (...args) {
|
|
736
765
|
const context = await original.apply(this, args);
|
|
737
|
-
|
|
766
|
+
const configuredHeaders = args[1]?.extraHTTPHeaders ?? {};
|
|
767
|
+
trackContext(context, configuredHeaders);
|
|
738
768
|
const controller = activeController;
|
|
739
769
|
if (controller)
|
|
740
|
-
await controller.registerContext(context,
|
|
770
|
+
await controller.registerContext(context, configuredHeaders).catch(() => undefined);
|
|
741
771
|
return context;
|
|
742
772
|
});
|
|
743
773
|
}
|
|
@@ -18,8 +18,10 @@ function belongsToProject(parentURL) {
|
|
|
18
18
|
return false;
|
|
19
19
|
if (!PROJECT_ROOT)
|
|
20
20
|
return parentURL.includes("/tests/");
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// Derive the URL the way Node derives parentURL, or the two never match on
|
|
22
|
+
// Windows: a hand-built `file://C:/...` is not the `file:///C:/...` that
|
|
23
|
+
// pathToFileURL produces, and every project module would read as foreign.
|
|
24
|
+
const projectURL = pathToFileURL(PROJECT_ROOT).href.replace(/\/?$/, "/");
|
|
23
25
|
const generatedURL = `${projectURL}.supercov/`;
|
|
24
26
|
return (parentURL.startsWith(projectURL) && !parentURL.startsWith(generatedURL));
|
|
25
27
|
}
|