supercov 4.21.0 → 4.23.0
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 +1 -0
- package/docs/cli.md +1 -0
- package/docs/supported-suites.md +90 -0
- package/docs/workspace-isolation.md +3 -2
- package/package.json +9 -9
- package/runtime/javascript/bunTest.mjs +185 -0
- package/runtime/javascript/provenance.mjs +1 -1
- package/runtime/javascript/runnerEvidence.mjs +7 -3
- package/runtime/javascript/runtime.mjs +4 -0
package/README.md
CHANGED
|
@@ -411,3 +411,4 @@ Ask questions, share what you measured, and talk to the people building Supercov
|
|
|
411
411
|
- [@Uhbif2](https://github.com/Uhbif2)
|
|
412
412
|
- [@maik-intellicoach](https://github.com/maik-intellicoach)
|
|
413
413
|
- [@untitaker](https://github.com/untitaker)
|
|
414
|
+
- [@artile](https://github.com/artile)
|
package/docs/cli.md
CHANGED
|
@@ -921,6 +921,7 @@ terminal or offline environment after the package has been downloaded.
|
|
|
921
921
|
| Variable | Use |
|
|
922
922
|
| --- | --- |
|
|
923
923
|
| `SUPERCOV_SOURCE_ROOTS` | Comma-separated directories or files that hold your own code, in any language; everything else is left out |
|
|
924
|
+
| `SUPERCOV_TEST_ROOTS` | For an Ant build whose `<batchtest>` directories cannot be read from its files: comma-separated directories that hold its tests |
|
|
924
925
|
| `SUPERCOV_TEST_KIND` | Label the wrapped command as a test level such as `unit` or `e2e` |
|
|
925
926
|
| `SUPERCOV_SOURCE_TOOLS` | Comma-separated names of tools the test command runs that read source as text, such as a linter or a spell checker, besides Biome, oxlint, dprint, cspell and knip. Each is run on your source as you wrote it instead of on the instrumented copy |
|
|
926
927
|
| `SUPERCOV_RUN_ID` | Set by Supercov, not by you: in every process a measured command starts, the run's id, so a test or harness can tell it is measured |
|
package/docs/supported-suites.md
CHANGED
|
@@ -52,6 +52,7 @@ Supercov reports the level it actually observed. It does not guess.
|
|
|
52
52
|
| Vitest | Exact per test, with setup execution kept separate; Browser Mode included |
|
|
53
53
|
| Jest | Exact per test, including parameterized tests, with the user's own configuration, setup files and reporters kept |
|
|
54
54
|
| `node:test` | Exact per test |
|
|
55
|
+
| `bun:test` (Bun 1.2.17 and 1.4.4 verified) | Exact test-body coverage, including concurrent and parameterized tests; lifecycle hooks recorded as setup |
|
|
55
56
|
| Mocha | Exact per test and retry, `--parallel` included, with setup execution kept separate |
|
|
56
57
|
| AVA | Exact per test, concurrent tests included, in worker threads or child processes |
|
|
57
58
|
| node-tap 15 and newer | Exact per subtest, nested and parallel (`t.jobs`) subtests included |
|
|
@@ -61,6 +62,28 @@ Supercov reports the level it actually observed. It does not guess.
|
|
|
61
62
|
One command may launch several runners. Supercov combines their evidence into
|
|
62
63
|
one run and preserves runner identity wherever the runner exposes it.
|
|
63
64
|
|
|
65
|
+
### Bun
|
|
66
|
+
|
|
67
|
+
Run `supercov -- bun test` or the package script that launches it. Supercov
|
|
68
|
+
loads a native Bun adapter through `BUN_OPTIONS`, preserving existing Bun
|
|
69
|
+
options, configured preloads, and the project's files. Named ESM imports,
|
|
70
|
+
namespace imports, and CommonJS `require('bun:test')` are supported. Bun ignores
|
|
71
|
+
Node's preload settings, so the Node adapter alone cannot attribute its tests.
|
|
72
|
+
|
|
73
|
+
Synchronous `describe` boundaries form the test title. Parameterized rows use
|
|
74
|
+
the registration's title template and original row index in Supercov's queries;
|
|
75
|
+
Bun keeps its own formatted reporter titles. Async test bodies retain their
|
|
76
|
+
identity across `await`, including concurrent execution. Lifecycle hooks have
|
|
77
|
+
separate setup identities and do not credit their coverage to the test body.
|
|
78
|
+
|
|
79
|
+
Callback completion supplies the recorded outcome. If Bun interrupts a callback
|
|
80
|
+
before it completes, Supercov preserves its named coverage with an **unknown**
|
|
81
|
+
outcome. Bun's final runner verdict can additionally reflect a timeout, hook
|
|
82
|
+
failure, assertion-count check, or unhandled error outside that callback; those
|
|
83
|
+
verdicts are not inferred from callback completion. The measured command's exit
|
|
84
|
+
status remains Bun's own. Coverage from an API copied by a module mock before
|
|
85
|
+
adaptation may remain unattributed.
|
|
86
|
+
|
|
64
87
|
### Vitest Browser Mode
|
|
65
88
|
|
|
66
89
|
Vitest Browser Mode runs the test file in a real browser. Supercov measures it
|
|
@@ -713,6 +736,9 @@ npx supercov -- npx playwright test
|
|
|
713
736
|
| Spock | Exact per feature, under the names Spock itself reports | — |
|
|
714
737
|
| TestNG | Exact per test, each data-provider invocation its own | — |
|
|
715
738
|
| JUnit 4 alone | Exact per test, through Vintage — see below | Maven or Gradle |
|
|
739
|
+
| JUnit 4 under Ant's `<junit>` | Exact per test, under the names Ant's XML report uses | Ant 1.10 — see below |
|
|
740
|
+
| Any platform engine under Ant's `<junitlauncher>` | Exact per test, through the platform listener | Ant 1.10 — see below |
|
|
741
|
+
| TestNG under Ant's `<testng>` | Exact per test, through the TestNG listener | TestNG 6.x with its Ant task — see below |
|
|
716
742
|
|
|
717
743
|
Multi-module builds are measured module by module: each compiles its own source
|
|
718
744
|
set and forks its own JVM, so each gets a runtime and records evidence of its
|
|
@@ -775,6 +801,70 @@ npx supercov -- ./gradlew test
|
|
|
775
801
|
npx supercov -- ./mvnw verify
|
|
776
802
|
```
|
|
777
803
|
|
|
804
|
+
### Ant and Ivy
|
|
805
|
+
|
|
806
|
+
A project with a `build.xml` (and no Maven or Gradle build) is an Ant build.
|
|
807
|
+
Ant has no layout convention, so Supercov reads the build: it follows the
|
|
808
|
+
`<import>` chain with the properties it defines, takes the directories each
|
|
809
|
+
`<batchtest>` or `<testclasses>` scans as the tests — or, where it scans
|
|
810
|
+
compiled classes, the sources the `<javac>` writing them compiles — and what
|
|
811
|
+
its other `<javac>` tasks compile as the code they test, leaving out the
|
|
812
|
+
directories the build writes into. Java elsewhere, such as the sources a test
|
|
813
|
+
compiles as its input, is left alone. Where a `<javac>` compiles from a path
|
|
814
|
+
the build makes only at run time, every other `src` directory holding Java is
|
|
815
|
+
taken as product code too.
|
|
816
|
+
When the build names its tests where that cannot be read — a path a custom
|
|
817
|
+
task builds at run time — say where they are:
|
|
818
|
+
|
|
819
|
+
```sh
|
|
820
|
+
SUPERCOV_TEST_ROOTS=test/src npx supercov -- ant test
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
`SUPERCOV_SOURCE_ROOTS` narrows the product code the same way as for any
|
|
824
|
+
project.
|
|
825
|
+
|
|
826
|
+
Ant's `<junit>` task runs JUnit 4 itself, not on the JUnit Platform, so in the
|
|
827
|
+
copy each `<junit>` task gets a result formatter of Supercov's next to its own
|
|
828
|
+
formatters. The task loads it in every JVM it forks — one per test class by
|
|
829
|
+
default — and it tells the runtime when each test starts and ends. Each JVM
|
|
830
|
+
writes its evidence at the end of its suite and on shutdown, so a JVM the
|
|
831
|
+
task stops for running past its timeout still leaves what it recorded, and
|
|
832
|
+
the run merges every one. The formatter is compiled by the copy's build just
|
|
833
|
+
before the task, against the task's own classpath and Ant's runtime. Tests
|
|
834
|
+
keep the names Ant's XML reports give them, `Class#method`; a test a suite
|
|
835
|
+
runs is named within it, `Suite#Class#method`, and a test two tasks or
|
|
836
|
+
filesets both run is numbered in the second (`[2]`). A test the task stops
|
|
837
|
+
for its timeout is recorded as failed, with what it ran until then. A build
|
|
838
|
+
that names no tests where they can be read keeps the `src/main` and
|
|
839
|
+
`src/test` convention.
|
|
840
|
+
|
|
841
|
+
Ant's `<junitlauncher>` runs tests on the JUnit Platform, so it gets the same
|
|
842
|
+
platform listener Maven and Gradle builds get, as a `<listener>` of the task,
|
|
843
|
+
compiled the same way against the task's classpath. TestNG's `<testng>` task
|
|
844
|
+
gets the TestNG listener in its own `listeners` attribute, beside any the
|
|
845
|
+
build names.
|
|
846
|
+
|
|
847
|
+
Many Ant builds import a shared harness from beside the project —
|
|
848
|
+
`<import file="../../../tools/master/build/common/common-build.xml"/>` — and
|
|
849
|
+
the `<junit>` task lives there. The copy is laid out at the project's place
|
|
850
|
+
within the closest directory holding it and everything its build imports,
|
|
851
|
+
with links standing for the rest, so `../` reaches what it reaches in the
|
|
852
|
+
checkout. A file outside the project that holds a `<junit>` task — the
|
|
853
|
+
harness, or a wrapper script's own build file beside it — is changed in the
|
|
854
|
+
copy's own version of that file; the original and the rest of the harness
|
|
855
|
+
are only read. Small files and directories beside the project are copied
|
|
856
|
+
rather than linked, so a wrapper script that writes beside itself writes into
|
|
857
|
+
the copy; anything larger than 4 MB stays a link.
|
|
858
|
+
|
|
859
|
+
Only the project's own sources are measured: modules resolved as jars, Ivy's
|
|
860
|
+
included, are not instrumented. Code that is not Java or Kotlin — XSLT,
|
|
861
|
+
sitemaps, scripts run by the JVM — is outside what the figure covers.
|
|
862
|
+
|
|
863
|
+
```sh
|
|
864
|
+
npx supercov -- ant test
|
|
865
|
+
npx supercov -- ../../../run-single-test.sh . ALL
|
|
866
|
+
```
|
|
867
|
+
|
|
778
868
|
## Containers, VMs, and remote execution
|
|
779
869
|
|
|
780
870
|
Supercov can collect from supported processes launched through a container, VM,
|
|
@@ -200,8 +200,9 @@ its settings with paths moved to the guest's mount, and the guest's coverage is
|
|
|
200
200
|
credited to the test that launched it. A guest started any other way, with none
|
|
201
201
|
of those settings, still runs the instrumented code: it reads the run's
|
|
202
202
|
settings from the mounted workspace itself, and its coverage counts for the run
|
|
203
|
-
as background coverage, credited to no test. The run summary's `
|
|
204
|
-
|
|
203
|
+
as background coverage, credited to no test. The run summary's `Unattributed`
|
|
204
|
+
line counts processes whose coverage has no named test identity. Local processes
|
|
205
|
+
can also appear here; this count does not establish container or VM execution.
|
|
205
206
|
|
|
206
207
|
Dependencies stay out of the instrumented copy. The root `node_modules` is
|
|
207
208
|
linked entry by entry to the project's own, so an environment that mounts the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.23.0",
|
|
4
4
|
"description": "Coverage, security and code quality for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coverage",
|
|
@@ -122,14 +122,14 @@
|
|
|
122
122
|
"test:windows-tls": "node scripts/windows-tls-test.mjs"
|
|
123
123
|
},
|
|
124
124
|
"optionalDependencies": {
|
|
125
|
-
"@supercov/cli-darwin-arm64": "4.
|
|
126
|
-
"@supercov/cli-darwin-x64": "4.
|
|
127
|
-
"@supercov/cli-linux-arm64-gnu": "4.
|
|
128
|
-
"@supercov/cli-linux-arm64-musl": "4.
|
|
129
|
-
"@supercov/cli-linux-x64-gnu": "4.
|
|
130
|
-
"@supercov/cli-linux-x64-musl": "4.
|
|
131
|
-
"@supercov/cli-win32-arm64": "4.
|
|
132
|
-
"@supercov/cli-win32-x64": "4.
|
|
125
|
+
"@supercov/cli-darwin-arm64": "4.23.0",
|
|
126
|
+
"@supercov/cli-darwin-x64": "4.23.0",
|
|
127
|
+
"@supercov/cli-linux-arm64-gnu": "4.23.0",
|
|
128
|
+
"@supercov/cli-linux-arm64-musl": "4.23.0",
|
|
129
|
+
"@supercov/cli-linux-x64-gnu": "4.23.0",
|
|
130
|
+
"@supercov/cli-linux-x64-musl": "4.23.0",
|
|
131
|
+
"@supercov/cli-win32-arm64": "4.23.0",
|
|
132
|
+
"@supercov/cli-win32-x64": "4.23.0"
|
|
133
133
|
},
|
|
134
134
|
"peerDependencies": {
|
|
135
135
|
"@playwright/test": ">=1.55.0",
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Bun does not load the Node --import preload. BUN_OPTIONS loads this before
|
|
2
|
+
// its test files and their configured preloads. Older Bun versions create a
|
|
3
|
+
// native API per file; newer versions share one. Adapt the factory and the
|
|
4
|
+
// existing API so both keep native test registration and execution semantics.
|
|
5
|
+
import * as runtime from "./runtime.mjs";
|
|
6
|
+
import { callerLocation, runnerExecutionScope, runnerTestId, writeRunnerEvidence } from "./runnerEvidence.mjs";
|
|
7
|
+
|
|
8
|
+
const adapted = new WeakSet();
|
|
9
|
+
const nativeJest = Bun.jest;
|
|
10
|
+
const attempts = new Map();
|
|
11
|
+
function writeEvidence(identity, status, scope, directory, phases = [], source) {
|
|
12
|
+
return writeRunnerEvidence(identity, status, scope, directory, phases, source, true);
|
|
13
|
+
}
|
|
14
|
+
const previousPhaseObserver = globalThis.__SUPERCOV_ASSERTION_PHASE_STARTED__;
|
|
15
|
+
globalThis.__SUPERCOV_ASSERTION_PHASE_STARTED__ = (scope, phase) => {
|
|
16
|
+
previousPhaseObserver?.(scope, phase);
|
|
17
|
+
const attempt = attempts.get(scope.attemptId);
|
|
18
|
+
if (attempt) {
|
|
19
|
+
attempt.phases.push(phase);
|
|
20
|
+
writeEvidence(attempt.identity, "unknown", scope, undefined, attempt.phases);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function adapt(api) {
|
|
24
|
+
if (adapted.has(api)) return api;
|
|
25
|
+
adapted.add(api);
|
|
26
|
+
const originals = { ...api };
|
|
27
|
+
let suites = [];
|
|
28
|
+
const registrations = new Map();
|
|
29
|
+
const evidenceDirectory = process.env.SUPERCOV_EVIDENCE_DIR;
|
|
30
|
+
|
|
31
|
+
function callbackIndex(args) {
|
|
32
|
+
return args.findLastIndex(value => typeof value === "function");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrap(original, kind, flags = {}, row = {}) {
|
|
36
|
+
const members = new Map();
|
|
37
|
+
function supercovBunRegistration(...args) {
|
|
38
|
+
const index = callbackIndex(args);
|
|
39
|
+
const callback = index < 0 ? undefined : args[index];
|
|
40
|
+
const name = row.name ?? (typeof args[0] === "string" ? args[0] : callback?.name || "anonymous test");
|
|
41
|
+
const location = row.location ?? callerLocation(supercovBunRegistration);
|
|
42
|
+
if (kind === "suite") {
|
|
43
|
+
if (!callback) return Reflect.apply(original, this, args);
|
|
44
|
+
const next = [...args];
|
|
45
|
+
next[index] = function () {
|
|
46
|
+
const values = [...arguments];
|
|
47
|
+
const parent = suites;
|
|
48
|
+
suites = [...parent, { name, flags }];
|
|
49
|
+
try { return Reflect.apply(callback, this, values); }
|
|
50
|
+
finally { suites = parent; }
|
|
51
|
+
};
|
|
52
|
+
return Reflect.apply(original, this, next);
|
|
53
|
+
}
|
|
54
|
+
const identity = {
|
|
55
|
+
runner: "bun:test",
|
|
56
|
+
name: kind === "test" ? [...suites.map(suite => suite.name), name].join(" > ") : `[${kind}] ${name}`,
|
|
57
|
+
...location,
|
|
58
|
+
...(location.line === undefined ? {} : { column: 1 }),
|
|
59
|
+
...(kind === "test" ? {} : { role: "setup" }),
|
|
60
|
+
};
|
|
61
|
+
const key = runnerTestId(identity);
|
|
62
|
+
identity.registrationOrdinal = registrations.get(key) ?? 0;
|
|
63
|
+
registrations.set(key, identity.registrationOrdinal + 1);
|
|
64
|
+
const effective = {};
|
|
65
|
+
for (const active of [...suites.map(suite => suite.flags), flags]) {
|
|
66
|
+
for (const [key, value] of Object.entries(active)) effective[key] ||= value;
|
|
67
|
+
}
|
|
68
|
+
if (!callback) {
|
|
69
|
+
const result = Reflect.apply(original, this, args);
|
|
70
|
+
writeEvidence(identity, effective.todo ? "todo" : "skipped", runnerExecutionScope(identity), evidenceDirectory);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
let attempt = 0;
|
|
74
|
+
const next = [...args];
|
|
75
|
+
next[index] = function () {
|
|
76
|
+
const values = [...arguments];
|
|
77
|
+
const current = { ...identity, retry: attempt++ };
|
|
78
|
+
const scope = runnerExecutionScope(current);
|
|
79
|
+
// Bun may terminate a timed-out callback without running its
|
|
80
|
+
// finally or Node exit hooks. Persist identity before execution,
|
|
81
|
+
// and use the runtime's per-turn transport instead of buffering
|
|
82
|
+
// the whole attempt until callback completion.
|
|
83
|
+
attempts.set(scope.attemptId, { identity: current, phases: [] });
|
|
84
|
+
writeEvidence(current, "unknown", scope, evidenceDirectory);
|
|
85
|
+
let finished = false;
|
|
86
|
+
function finish(failed = false) {
|
|
87
|
+
if (finished) return;
|
|
88
|
+
finished = true;
|
|
89
|
+
attempts.delete(scope.attemptId);
|
|
90
|
+
const status = effective.todo ? "todo" : failed === Boolean(effective.failing) ? "passed" : "failed";
|
|
91
|
+
writeEvidence(current, status, scope, evidenceDirectory, runtime.takeNodeAssertionPhases(scope));
|
|
92
|
+
}
|
|
93
|
+
const actual = [...values];
|
|
94
|
+
const bound = row.bound ?? 0;
|
|
95
|
+
if (callback.length > bound && typeof actual[bound] === "function") {
|
|
96
|
+
const done = actual[bound];
|
|
97
|
+
actual[bound] = error => { finish(error != null); return done(error); };
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const result = runtime.withCoverageCarrier({ version: 1, scope }, () => Reflect.apply(callback, this, actual));
|
|
101
|
+
if (result && typeof result.then === "function") {
|
|
102
|
+
return Promise.resolve(result).then(value => { finish(); return value; }, error => { finish(true); throw error; });
|
|
103
|
+
}
|
|
104
|
+
if (callback.length <= bound) finish();
|
|
105
|
+
return result;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
finish(true);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
Object.defineProperty(next[index], "length", { value: callback.length });
|
|
112
|
+
const result = Reflect.apply(original, this, next);
|
|
113
|
+
if (effective.skip || effective.todo) {
|
|
114
|
+
writeEvidence(identity, effective.todo ? "todo" : "skipped", runnerExecutionScope(identity), evidenceDirectory);
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
return new Proxy(supercovBunRegistration, {
|
|
119
|
+
get(target, property) {
|
|
120
|
+
if (["call", "apply", "bind"].includes(property)) return Reflect.get(target, property);
|
|
121
|
+
|
|
122
|
+
if (members.has(property)) return members.get(property);
|
|
123
|
+
const member = Reflect.get(original, property, original);
|
|
124
|
+
if (typeof member !== "function") return member;
|
|
125
|
+
if (property === "each") {
|
|
126
|
+
const each = table => {
|
|
127
|
+
const nativeEach = Reflect.apply(member, original, [table]);
|
|
128
|
+
if (!Array.isArray(table) || table.length === 0) return nativeEach;
|
|
129
|
+
return function supercovBunEach(...args) {
|
|
130
|
+
const location = callerLocation(supercovBunEach);
|
|
131
|
+
for (const [index, value] of table.entries()) {
|
|
132
|
+
const bound = Array.isArray(value) ? value.length : 1;
|
|
133
|
+
const next = [...args];
|
|
134
|
+
// Keep Bun's own formatting and binding. Only %# needs
|
|
135
|
+
// the original index when registering one row at a time.
|
|
136
|
+
let consumed = 0;
|
|
137
|
+
if (typeof next[0] === "string") next[0] = next[0].replace(/%([sidfjop#%])/g, (token, format) => {
|
|
138
|
+
if (consumed >= bound) return token;
|
|
139
|
+
if (format === "#") return String(index);
|
|
140
|
+
if (format !== "%") consumed++;
|
|
141
|
+
return token;
|
|
142
|
+
});
|
|
143
|
+
const register = Reflect.apply(member, original, [[value]]);
|
|
144
|
+
Reflect.apply(wrap(register, kind, flags, {
|
|
145
|
+
location, bound,
|
|
146
|
+
name: `${args[0]} [row ${index + 1}]`,
|
|
147
|
+
}), this, next);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
members.set(property, each);
|
|
152
|
+
return each;
|
|
153
|
+
}
|
|
154
|
+
if (["skip", "todo", "only", "concurrent", "serial", "failing"].includes(property)) {
|
|
155
|
+
const wrapped = wrap(member, kind, { ...flags, [property]: true });
|
|
156
|
+
members.set(property, wrapped);
|
|
157
|
+
return wrapped;
|
|
158
|
+
}
|
|
159
|
+
if (["if", "skipIf", "todoIf", "failingIf", "concurrentIf", "serialIf"].includes(property)) {
|
|
160
|
+
return (...values) => wrap(Reflect.apply(member, original, values), kind, {
|
|
161
|
+
...flags,
|
|
162
|
+
[property === "if" ? "skip" : property.slice(0, -2)]: flags[property === "if" ? "skip" : property.slice(0, -2)] || (property === "if" ? !values[0] : Boolean(values[0])),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return member.bind(original);
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
api.test = api.it = wrap(originals.test, "test");
|
|
171
|
+
api.describe = wrap(originals.describe, "suite");
|
|
172
|
+
for (const [name, kind, flags] of [
|
|
173
|
+
["xtest", "test", { skip: true }], ["xit", "test", { skip: true }],
|
|
174
|
+
["xdescribe", "suite", { skip: true }],
|
|
175
|
+
["beforeAll", "beforeAll", {}], ["afterAll", "afterAll", {}],
|
|
176
|
+
["beforeEach", "beforeEach", {}], ["afterEach", "afterEach", {}],
|
|
177
|
+
]) {
|
|
178
|
+
if (typeof originals[name] === "function") api[name] = wrap(originals[name], kind, flags);
|
|
179
|
+
}
|
|
180
|
+
return api;
|
|
181
|
+
}
|
|
182
|
+
if (typeof nativeJest === "function") {
|
|
183
|
+
Bun.jest = function (...args) { return adapt(Reflect.apply(nativeJest, this, args)); };
|
|
184
|
+
adapt(Reflect.apply(nativeJest, Bun, [import.meta.path]));
|
|
185
|
+
}
|
|
@@ -139,7 +139,7 @@ function inferredProvenance({ runner, file, project, explicitKind, }) {
|
|
|
139
139
|
source: "path",
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
|
-
const defaultKind = runner === "vitest" || runner === "jest" || runner === "node:test" || runner === "mocha" || runner === "ava" || runner === "tap"
|
|
142
|
+
const defaultKind = runner === "vitest" || runner === "jest" || runner === "node:test" || runner === "bun:test" || runner === "mocha" || runner === "ava" || runner === "tap"
|
|
143
143
|
? "unit"
|
|
144
144
|
: "unknown";
|
|
145
145
|
return {
|
|
@@ -2,7 +2,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import { appendEvidenceRecord } from "./atomic.mjs";
|
|
5
|
+
import { appendEvidenceRecord, atomicWriteFileSync } from "./atomic.mjs";
|
|
6
6
|
import { inferTestProvenance, testTags } from "./provenance.mjs";
|
|
7
7
|
import { serverEvidencePath } from "./transport.mjs";
|
|
8
8
|
|
|
@@ -105,7 +105,7 @@ export function readScopedServerEvidence(scope, evidencePath = serverEvidencePat
|
|
|
105
105
|
return [];
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOverride, phases = [], serverEvidenceSource) {
|
|
108
|
+
export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOverride, phases = [], serverEvidenceSource, snapshot = false) {
|
|
109
109
|
const evidenceDirectory = evidenceDirectoryOverride ?? process.env["SUPERCOV_EVIDENCE_DIR"];
|
|
110
110
|
if (!evidenceDirectory)
|
|
111
111
|
return;
|
|
@@ -132,7 +132,11 @@ export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOv
|
|
|
132
132
|
? readScopedServerEvidence(scope, serverEvidenceSource)
|
|
133
133
|
: readScopedServerEvidence(scope),
|
|
134
134
|
};
|
|
135
|
-
|
|
135
|
+
if (snapshot) {
|
|
136
|
+
atomicWriteFileSync(resolve(process.cwd(), evidenceDirectory, `attempt-${scope.attemptId}`, "mcdc.json"), JSON.stringify(payload));
|
|
137
|
+
} else {
|
|
138
|
+
appendEvidenceRecord(resolve(process.cwd(), evidenceDirectory), identity.runner.replace(/[^A-Za-z0-9_-]/g, "_"), payload);
|
|
139
|
+
}
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
// A failing test's stack as its author would have seen it, for the runners
|
|
@@ -1251,6 +1251,8 @@ function withNodeAssertionPhase(operation, source, callback) {
|
|
|
1251
1251
|
});
|
|
1252
1252
|
attempt.phases.push(phase);
|
|
1253
1253
|
attempt.phaseIds.add(phase.id);
|
|
1254
|
+
const phaseObserver = runtimeGlobal.__SUPERCOV_ASSERTION_PHASE_STARTED__;
|
|
1255
|
+
if (phaseObserver) phaseObserver(scope, phase);
|
|
1254
1256
|
try {
|
|
1255
1257
|
const result = withStackRoom(() => withCoverageCarrier({ version: 1, scope, phaseId: phase.id }, callback));
|
|
1256
1258
|
if (result && typeof result.then === "function")
|
|
@@ -1326,6 +1328,8 @@ function openNodeAssertionPhase(operation, source) {
|
|
|
1326
1328
|
});
|
|
1327
1329
|
attempt.phases.push(phase);
|
|
1328
1330
|
attempt.phaseIds.add(phase.id);
|
|
1331
|
+
const phaseObserver = runtimeGlobal.__SUPERCOV_ASSERTION_PHASE_STARTED__;
|
|
1332
|
+
if (phaseObserver) phaseObserver(scope, phase);
|
|
1329
1333
|
carrier = { version: 1, scope, phaseId: phase.id };
|
|
1330
1334
|
finish = (error) => finishAssertionPhase(phase, error);
|
|
1331
1335
|
}
|