@continuous-excellence/testmints-js 0.0.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/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +119 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.d.ts +78 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +250 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/reporting.d.ts +40 -0
- package/dist/reporting.d.ts.map +1 -0
- package/dist/reporting.js +115 -0
- package/dist/reporting.js.map +1 -0
- package/dist/vitest.d.ts +11 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +109 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Continuous Excellence
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Testmints
|
|
2
|
+
|
|
3
|
+
> **Status: incubating and experimental. Do not integrate Testmints into a project at this time.**
|
|
4
|
+
> Its API, behavior, artifact format, package availability, and release policy may change or be
|
|
5
|
+
> withdrawn without compatibility guarantees.
|
|
6
|
+
|
|
7
|
+
Testmints gives ordinary tests an explicit shape: **setup → exercise → verify → teardown**.
|
|
8
|
+
It owns phase sequencing and locally acquired resource cleanup, while your test runner and assertion
|
|
9
|
+
library remain yours.
|
|
10
|
+
|
|
11
|
+
This is an early implementation of `@continuous-excellence/testmints-js`. It supports a
|
|
12
|
+
runner-independent executor, Vitest registration, and an opt-in local phase stream. Cancellation
|
|
13
|
+
policy, task groups, browser reporting, and additional runners are intentionally not available yet.
|
|
14
|
+
|
|
15
|
+
The test suite is first-class usage documentation: each test should show a realistic scenario while
|
|
16
|
+
also proving the library behavior it relies on.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { expect } from 'vitest'
|
|
20
|
+
import { mintTest } from '@continuous-excellence/testmints-js/vitest'
|
|
21
|
+
|
|
22
|
+
mintTest('15 produces FizzBuzz', {
|
|
23
|
+
setup: () => 15,
|
|
24
|
+
exercise: (input) => fizzBuzz(input),
|
|
25
|
+
verify: (output) => expect(output).toBe('FizzBuzz'),
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Local phase artifacts with Vitest
|
|
30
|
+
|
|
31
|
+
Enable the Node/Vitest reporting plugin in `vitest.config.ts`. Existing `mintTest` calls need no
|
|
32
|
+
per-test configuration.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { defineConfig } from 'vitest/config'
|
|
36
|
+
import { testmintsVitest } from '@continuous-excellence/testmints-js/vitest'
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
plugins: [testmintsVitest()],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Each Vitest process writes a run directory under `.testmints/` (or the supplied `outputDir`). Each
|
|
44
|
+
worker appends only to its own NDJSON file. The run manifest records whether reporting completed;
|
|
45
|
+
reporting problems are visible but never turn a test pass into a failure or prevent cleanup.
|
|
46
|
+
|
|
47
|
+
Inspect a completed run with the package command:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
testmints inspect .testmints/run-…
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
a member can save their display name — FAILED
|
|
55
|
+
setup 41 ms passed
|
|
56
|
+
exercise 184 ms passed
|
|
57
|
+
verify 2 ms failed — expected "Ada", received "Grace"
|
|
58
|
+
teardown 18 ms passed
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The first reporter is deliberately Node/Vitest-only. It records Testmints phase boundaries and
|
|
62
|
+
does not inspect assertion libraries, capture arbitrary data, or promise cancellation beyond the
|
|
63
|
+
runner signal passed to active phases.
|
|
64
|
+
|
|
65
|
+
Resources acquired through `scope.using()` survive verification and are released in reverse order,
|
|
66
|
+
including after a failed setup, exercise, or verification.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
mintTest('a member can save their display name', {
|
|
70
|
+
setup: async ({ scope }) => ({
|
|
71
|
+
account: await scope.using('temporary account', createAccount, deleteAccount),
|
|
72
|
+
displayName: 'Ada',
|
|
73
|
+
}),
|
|
74
|
+
exercise: ({ account, displayName }) => updateProfile(account.id, { displayName }),
|
|
75
|
+
verify: (profile) => expect(profile.displayName).toBe('Ada'),
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
`npm run check` runs formatting/lint checks, type fixtures, and the test suite. Every test is both
|
|
82
|
+
evidence for the library and an example of its intended use. Core-contract scenarios also use
|
|
83
|
+
`mintTest`, exercising lower-level bootstrap primitives within that same test shape.
|
|
84
|
+
|
|
85
|
+
Contributors should begin with [the project philosophy](docs/project-philosophy.md). The
|
|
86
|
+
[library direction log](docs/library-direction.md) captures longer-term design questions that are
|
|
87
|
+
not current commitments. Maintainers can follow the [publishing guide](docs/publishing.md) for the
|
|
88
|
+
one-time `0.0.0` bootstrap and the subsequent trunk-based release flow.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAoEA,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkEhE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { readManifest, } from "./reporting.js";
|
|
6
|
+
function usage() {
|
|
7
|
+
console.error("Usage: testmints inspect <artifact-dir>");
|
|
8
|
+
process.exit(2);
|
|
9
|
+
}
|
|
10
|
+
function duration(durationMs) {
|
|
11
|
+
return `${Math.round(durationMs)} ms`;
|
|
12
|
+
}
|
|
13
|
+
async function readEvents(directory, files) {
|
|
14
|
+
const events = [];
|
|
15
|
+
let incomplete = false;
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
let text;
|
|
18
|
+
try {
|
|
19
|
+
text = await readFile(resolve(directory, "events", file), "utf8");
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
incomplete = true;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const lines = text.split("\n");
|
|
26
|
+
for (const [index, line] of lines.entries()) {
|
|
27
|
+
if (line === "")
|
|
28
|
+
continue;
|
|
29
|
+
try {
|
|
30
|
+
events.push(JSON.parse(line));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
if (index !== lines.length - 1)
|
|
34
|
+
incomplete = true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { events, incomplete };
|
|
39
|
+
}
|
|
40
|
+
function phaseEvents(events) {
|
|
41
|
+
return events.filter((event) => event.kind === "phase.end");
|
|
42
|
+
}
|
|
43
|
+
async function readRunnerResults(directory) {
|
|
44
|
+
try {
|
|
45
|
+
return (await readFile(resolve(directory, "events", "runner.ndjson"), "utf8"))
|
|
46
|
+
.trim()
|
|
47
|
+
.split("\n")
|
|
48
|
+
.filter((line) => line !== "")
|
|
49
|
+
.map((line) => JSON.parse(line));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function inspect(directory) {
|
|
56
|
+
let manifest;
|
|
57
|
+
try {
|
|
58
|
+
manifest = await readManifest(directory);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error(`Cannot read Testmints manifest: ${String(error)}`);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
const { events, incomplete: malformed } = await readEvents(directory, manifest.producerFiles);
|
|
65
|
+
const runnerResults = new Map((await readRunnerResults(directory)).map((result) => [
|
|
66
|
+
result.testId,
|
|
67
|
+
result,
|
|
68
|
+
]));
|
|
69
|
+
const attempts = new Map();
|
|
70
|
+
for (const event of events) {
|
|
71
|
+
const grouped = attempts.get(event.attemptId) ?? [];
|
|
72
|
+
grouped.push(event);
|
|
73
|
+
attempts.set(event.attemptId, grouped);
|
|
74
|
+
}
|
|
75
|
+
let failed = false;
|
|
76
|
+
let interrupted = false;
|
|
77
|
+
for (const attempt of attempts.values()) {
|
|
78
|
+
attempt.sort((left, right) => left.seq - right.seq);
|
|
79
|
+
const started = attempt.find((event) => event.kind === "attempt.start");
|
|
80
|
+
const ended = attempt.find((event) => event.kind === "attempt.end");
|
|
81
|
+
if (!started)
|
|
82
|
+
continue;
|
|
83
|
+
if (!ended)
|
|
84
|
+
interrupted = true;
|
|
85
|
+
const runner = runnerResults.get(started.testId);
|
|
86
|
+
const attemptFailed = (ended?.kind === "attempt.end" && ended.status === "failed") ||
|
|
87
|
+
runner?.status === "failed";
|
|
88
|
+
failed ||= attemptFailed;
|
|
89
|
+
console.log(`${started.title} — ${attemptFailed ? "FAILED" : "passed"}`);
|
|
90
|
+
for (const phase of phaseEvents(attempt)) {
|
|
91
|
+
const detail = phase.status === "failed"
|
|
92
|
+
? ` — ${phase.error?.message ?? "failed"}`
|
|
93
|
+
: phase.status === "skipped"
|
|
94
|
+
? ` — ${phase.reason ?? "skipped"}`
|
|
95
|
+
: "";
|
|
96
|
+
console.log(` ${phase.phase.padEnd(10)} ${duration(phase.durationMs).padStart(7)} ${phase.status}${detail}`);
|
|
97
|
+
}
|
|
98
|
+
if (runner?.status === "failed" &&
|
|
99
|
+
ended?.kind === "attempt.end" &&
|
|
100
|
+
ended.status === "passed") {
|
|
101
|
+
console.log(` runner failed — ${runner.error?.message ?? "failed"}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const incomplete = malformed ||
|
|
105
|
+
interrupted ||
|
|
106
|
+
manifest.status !== "complete" ||
|
|
107
|
+
(manifest.failureFiles?.length ?? 0) > 0;
|
|
108
|
+
if (incomplete)
|
|
109
|
+
console.error("Testmints artifacts are incomplete.");
|
|
110
|
+
return failed || incomplete ? 1 : 0;
|
|
111
|
+
}
|
|
112
|
+
const invokedPath = process.argv[1];
|
|
113
|
+
if (invokedPath !== undefined &&
|
|
114
|
+
import.meta.url === pathToFileURL(await realpath(invokedPath)).href) {
|
|
115
|
+
if (process.argv[2] !== "inspect" || process.argv[3] === undefined)
|
|
116
|
+
usage();
|
|
117
|
+
process.exitCode = await inspect(resolve(process.argv[3]));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type Awaitable<T> = T | PromiseLike<T>;
|
|
2
|
+
export type PhaseName = "setup" | "exercise" | "verify" | "teardown";
|
|
3
|
+
export type PhaseStatus = "passed" | "failed" | "skipped";
|
|
4
|
+
export interface EventContext {
|
|
5
|
+
runId: string;
|
|
6
|
+
producerId: string;
|
|
7
|
+
attemptId: string;
|
|
8
|
+
testId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EventError {
|
|
12
|
+
name: string;
|
|
13
|
+
message: string;
|
|
14
|
+
stack?: string;
|
|
15
|
+
}
|
|
16
|
+
interface EventBase extends EventContext {
|
|
17
|
+
v: 1;
|
|
18
|
+
seq: number;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AttemptStartedEvent extends EventBase {
|
|
22
|
+
kind: "attempt.start";
|
|
23
|
+
}
|
|
24
|
+
export interface AttemptEndedEvent extends EventBase {
|
|
25
|
+
kind: "attempt.end";
|
|
26
|
+
status: Exclude<PhaseStatus, "skipped">;
|
|
27
|
+
error?: EventError;
|
|
28
|
+
}
|
|
29
|
+
export interface PhaseStartedEvent extends EventBase {
|
|
30
|
+
kind: "phase.start";
|
|
31
|
+
phase: PhaseName;
|
|
32
|
+
}
|
|
33
|
+
export interface PhaseEndedEvent extends EventBase {
|
|
34
|
+
kind: "phase.end";
|
|
35
|
+
phase: PhaseName;
|
|
36
|
+
status: PhaseStatus;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
reason?: string;
|
|
39
|
+
error?: EventError;
|
|
40
|
+
}
|
|
41
|
+
export type TestmintEvent = AttemptStartedEvent | AttemptEndedEvent | PhaseStartedEvent | PhaseEndedEvent;
|
|
42
|
+
/** Observational only: a sink can never alter test execution. */
|
|
43
|
+
export interface EventSink {
|
|
44
|
+
emit(event: TestmintEvent): void | PromiseLike<void>;
|
|
45
|
+
}
|
|
46
|
+
export interface PhaseTools {
|
|
47
|
+
readonly signal: AbortSignal;
|
|
48
|
+
}
|
|
49
|
+
export interface CleanupTools extends PhaseTools {
|
|
50
|
+
}
|
|
51
|
+
export interface ResourceScope {
|
|
52
|
+
using<T>(name: string, acquire: () => Awaitable<T>, release: (resource: T, tools: CleanupTools) => Awaitable<unknown>): Promise<T>;
|
|
53
|
+
defer(name: string, release: (tools: CleanupTools) => Awaitable<unknown>): void;
|
|
54
|
+
}
|
|
55
|
+
export interface SetupTools extends PhaseTools {
|
|
56
|
+
readonly scope: ResourceScope;
|
|
57
|
+
}
|
|
58
|
+
export interface ExerciseTools extends PhaseTools {
|
|
59
|
+
readonly scope: ResourceScope;
|
|
60
|
+
}
|
|
61
|
+
export interface TestmintDefinition<Input, Output> {
|
|
62
|
+
setup: (tools: SetupTools) => Awaitable<Input>;
|
|
63
|
+
exercise: (input: Input, tools: ExerciseTools) => Awaitable<Output>;
|
|
64
|
+
verify: (output: Output, input: Input, tools: PhaseTools) => Awaitable<unknown>;
|
|
65
|
+
teardown?: (input: Input, tools: CleanupTools) => Awaitable<unknown>;
|
|
66
|
+
}
|
|
67
|
+
export type ValidatedDefinition<Input, Output> = Readonly<TestmintDefinition<Input, Output>>;
|
|
68
|
+
export interface ExecuteOptions {
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
sink?: EventSink;
|
|
71
|
+
eventContext?: EventContext;
|
|
72
|
+
/** Supplies a producer-wide event sequence when reporting spans attempts. */
|
|
73
|
+
nextEventSequence?: () => number;
|
|
74
|
+
}
|
|
75
|
+
export declare function validateDefinition<Input, Output>(candidate: TestmintDefinition<Input, Output>): ValidatedDefinition<Input, Output>;
|
|
76
|
+
export declare function executeDefinition<Input, Output>(definition: ValidatedDefinition<Input, Output>, options?: ExecuteOptions): Promise<void>;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
const phaseKeys = new Set(["setup", "exercise", "verify", "teardown"]);
|
|
2
|
+
function isDefinitionCandidate(value) {
|
|
3
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function requiredCallback(definition, phase) {
|
|
6
|
+
const callback = definition[phase];
|
|
7
|
+
if (typeof callback !== "function") {
|
|
8
|
+
throw new TypeError(`Testmint definition requires a callable ${phase} phase.`);
|
|
9
|
+
}
|
|
10
|
+
return (...arguments_) => callback(...arguments_);
|
|
11
|
+
}
|
|
12
|
+
function optionalCallback(definition) {
|
|
13
|
+
const callback = definition.teardown;
|
|
14
|
+
if (callback === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (typeof callback !== "function") {
|
|
17
|
+
throw new TypeError("Testmint teardown must be a function when provided.");
|
|
18
|
+
}
|
|
19
|
+
return (...arguments_) => callback(...arguments_);
|
|
20
|
+
}
|
|
21
|
+
export function validateDefinition(candidate) {
|
|
22
|
+
if (!isDefinitionCandidate(candidate)) {
|
|
23
|
+
throw new TypeError("A Testmint definition must be an object.");
|
|
24
|
+
}
|
|
25
|
+
const definition = candidate;
|
|
26
|
+
for (const key of Object.keys(definition)) {
|
|
27
|
+
if (!phaseKeys.has(key)) {
|
|
28
|
+
throw new TypeError(`Unknown Testmint definition key: ${key}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const setup = requiredCallback(definition, "setup");
|
|
32
|
+
const exercise = requiredCallback(definition, "exercise");
|
|
33
|
+
const verify = requiredCallback(definition, "verify");
|
|
34
|
+
const teardown = optionalCallback(definition);
|
|
35
|
+
const validated = {
|
|
36
|
+
setup: (tools) => setup(tools),
|
|
37
|
+
exercise: (input, tools) => exercise(input, tools),
|
|
38
|
+
verify: (output, input, tools) => verify(output, input, tools),
|
|
39
|
+
};
|
|
40
|
+
return Object.freeze(teardown === undefined
|
|
41
|
+
? validated
|
|
42
|
+
: {
|
|
43
|
+
...validated,
|
|
44
|
+
teardown: (input, tools) => teardown(input, tools),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
class AttemptScope {
|
|
48
|
+
#releases = [];
|
|
49
|
+
#pending = new Set();
|
|
50
|
+
#closing = false;
|
|
51
|
+
using(name, acquire, release) {
|
|
52
|
+
this.assertOpen();
|
|
53
|
+
const pending = Promise.resolve()
|
|
54
|
+
.then(acquire)
|
|
55
|
+
.then((resource) => {
|
|
56
|
+
this.#releases.push({
|
|
57
|
+
name,
|
|
58
|
+
release: (tools) => release(resource, tools),
|
|
59
|
+
});
|
|
60
|
+
return resource;
|
|
61
|
+
});
|
|
62
|
+
this.#pending.add(pending);
|
|
63
|
+
void pending
|
|
64
|
+
.finally(() => this.#pending.delete(pending))
|
|
65
|
+
.catch(() => undefined);
|
|
66
|
+
return pending;
|
|
67
|
+
}
|
|
68
|
+
defer(name, release) {
|
|
69
|
+
this.assertOpen();
|
|
70
|
+
this.#releases.push({ name, release });
|
|
71
|
+
}
|
|
72
|
+
async close(tools) {
|
|
73
|
+
if (this.#closing)
|
|
74
|
+
return [];
|
|
75
|
+
this.#closing = true;
|
|
76
|
+
await Promise.allSettled([...this.#pending]);
|
|
77
|
+
const errors = [];
|
|
78
|
+
for (const registration of [...this.#releases].reverse()) {
|
|
79
|
+
try {
|
|
80
|
+
await registration.release(tools);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
errors.push(error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return errors;
|
|
87
|
+
}
|
|
88
|
+
assertOpen() {
|
|
89
|
+
if (this.#closing)
|
|
90
|
+
throw new Error("Cannot acquire or defer a resource after cleanup has begun.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function eventError(error) {
|
|
94
|
+
if (error instanceof Error) {
|
|
95
|
+
return {
|
|
96
|
+
name: error.name,
|
|
97
|
+
message: error.message,
|
|
98
|
+
...(error.stack === undefined ? {} : { stack: error.stack }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return { name: "Error", message: String(error) };
|
|
102
|
+
}
|
|
103
|
+
class EventEmitter {
|
|
104
|
+
sink;
|
|
105
|
+
context;
|
|
106
|
+
nextSequence;
|
|
107
|
+
#sequence = 0;
|
|
108
|
+
constructor(sink, context, nextSequence) {
|
|
109
|
+
this.sink = sink;
|
|
110
|
+
this.context = context;
|
|
111
|
+
this.nextSequence = nextSequence;
|
|
112
|
+
}
|
|
113
|
+
async emit(event) {
|
|
114
|
+
if (!this.sink)
|
|
115
|
+
return;
|
|
116
|
+
const complete = {
|
|
117
|
+
...(this.context ?? {
|
|
118
|
+
runId: "local",
|
|
119
|
+
producerId: "local",
|
|
120
|
+
attemptId: "local",
|
|
121
|
+
testId: "local",
|
|
122
|
+
title: "local",
|
|
123
|
+
}),
|
|
124
|
+
...event,
|
|
125
|
+
v: 1,
|
|
126
|
+
seq: this.nextSequence?.() ?? this.#sequence++,
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
};
|
|
129
|
+
try {
|
|
130
|
+
await this.sink.emit(complete);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Instrumentation is not allowed to affect test behavior in this slice.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function runPhase(phase, emitter, callback) {
|
|
138
|
+
const startedAt = performance.now();
|
|
139
|
+
await emitter.emit({ kind: "phase.start", phase });
|
|
140
|
+
try {
|
|
141
|
+
const value = await callback();
|
|
142
|
+
await emitter.emit({
|
|
143
|
+
kind: "phase.end",
|
|
144
|
+
phase,
|
|
145
|
+
status: "passed",
|
|
146
|
+
durationMs: performance.now() - startedAt,
|
|
147
|
+
});
|
|
148
|
+
return { ok: true, value };
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
await emitter.emit({
|
|
152
|
+
kind: "phase.end",
|
|
153
|
+
phase,
|
|
154
|
+
status: "failed",
|
|
155
|
+
durationMs: performance.now() - startedAt,
|
|
156
|
+
error: eventError(error),
|
|
157
|
+
});
|
|
158
|
+
return { ok: false, error };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function skipPhase(emitter, phase, reason) {
|
|
162
|
+
await emitter.emit({
|
|
163
|
+
kind: "phase.end",
|
|
164
|
+
phase,
|
|
165
|
+
status: "skipped",
|
|
166
|
+
durationMs: 0,
|
|
167
|
+
reason,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
export async function executeDefinition(definition, options = {}) {
|
|
171
|
+
const scope = new AttemptScope();
|
|
172
|
+
const emitter = new EventEmitter(options.sink, options.eventContext, options.nextEventSequence);
|
|
173
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
174
|
+
const setupTools = { signal, scope };
|
|
175
|
+
const exerciseTools = { signal, scope };
|
|
176
|
+
const phaseTools = { signal };
|
|
177
|
+
const cleanupTools = { signal: new AbortController().signal };
|
|
178
|
+
let completedSetup;
|
|
179
|
+
let primaryError;
|
|
180
|
+
const cleanupErrors = [];
|
|
181
|
+
await emitter.emit({ kind: "attempt.start" });
|
|
182
|
+
const setup = await runPhase("setup", emitter, () => definition.setup(setupTools));
|
|
183
|
+
if (setup.ok) {
|
|
184
|
+
const input = setup.value;
|
|
185
|
+
completedSetup = { input };
|
|
186
|
+
const exercise = await runPhase("exercise", emitter, () => definition.exercise(input, exerciseTools));
|
|
187
|
+
if (exercise.ok) {
|
|
188
|
+
const verify = await runPhase("verify", emitter, () => definition.verify(exercise.value, input, phaseTools));
|
|
189
|
+
if (!verify.ok)
|
|
190
|
+
primaryError = verify.error;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
primaryError = exercise.error;
|
|
194
|
+
await skipPhase(emitter, "verify", "exercise failed");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
primaryError = setup.error;
|
|
199
|
+
await skipPhase(emitter, "exercise", "setup failed");
|
|
200
|
+
await skipPhase(emitter, "verify", "setup failed");
|
|
201
|
+
}
|
|
202
|
+
const teardownStartedAt = performance.now();
|
|
203
|
+
await emitter.emit({ kind: "phase.start", phase: "teardown" });
|
|
204
|
+
if (completedSetup && definition.teardown) {
|
|
205
|
+
try {
|
|
206
|
+
await definition.teardown(completedSetup.input, cleanupTools);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
cleanupErrors.push(error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
cleanupErrors.push(...(await scope.close(cleanupTools)));
|
|
213
|
+
if (cleanupErrors.length === 0) {
|
|
214
|
+
await emitter.emit({
|
|
215
|
+
kind: "phase.end",
|
|
216
|
+
phase: "teardown",
|
|
217
|
+
status: "passed",
|
|
218
|
+
durationMs: performance.now() - teardownStartedAt,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
const teardownError = cleanupErrors.length === 1
|
|
223
|
+
? cleanupErrors[0]
|
|
224
|
+
: new AggregateError(cleanupErrors, "Testmint cleanup failed.");
|
|
225
|
+
await emitter.emit({
|
|
226
|
+
kind: "phase.end",
|
|
227
|
+
phase: "teardown",
|
|
228
|
+
status: "failed",
|
|
229
|
+
durationMs: performance.now() - teardownStartedAt,
|
|
230
|
+
error: eventError(teardownError),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const finalError = primaryError === undefined && cleanupErrors.length === 0
|
|
234
|
+
? undefined
|
|
235
|
+
: primaryError === undefined
|
|
236
|
+
? cleanupErrors.length === 1
|
|
237
|
+
? cleanupErrors[0]
|
|
238
|
+
: new AggregateError(cleanupErrors, "Testmint execution and cleanup failed.")
|
|
239
|
+
: cleanupErrors.length === 0
|
|
240
|
+
? primaryError
|
|
241
|
+
: new AggregateError([primaryError, ...cleanupErrors], "Testmint execution and cleanup failed.");
|
|
242
|
+
await emitter.emit({
|
|
243
|
+
kind: "attempt.end",
|
|
244
|
+
status: finalError === undefined ? "passed" : "failed",
|
|
245
|
+
...(finalError === undefined ? {} : { error: eventError(finalError) }),
|
|
246
|
+
});
|
|
247
|
+
if (finalError !== undefined)
|
|
248
|
+
throw finalError;
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { type Awaitable, type CleanupTools, type EventContext, type EventError, type EventSink, type ExecuteOptions, type ExerciseTools, executeDefinition, type PhaseName, type PhaseTools, type ResourceScope, type SetupTools, type TestmintDefinition, type TestmintEvent, type ValidatedDefinition, validateDefinition, } from "./core.js";
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,iBAAiB,EACjB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,kBAAkB,GAClB,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAQN,iBAAiB,EAQjB,kBAAkB,GAClB,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EventSink, TestmintEvent } from "./core.js";
|
|
2
|
+
export interface ReportingContext {
|
|
3
|
+
readonly outputDir: string;
|
|
4
|
+
readonly runId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RunManifest {
|
|
7
|
+
v: 1;
|
|
8
|
+
runId: string;
|
|
9
|
+
status: "running" | "complete" | "incomplete";
|
|
10
|
+
producerFiles: string[];
|
|
11
|
+
failureFiles: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface RunnerResultEvent {
|
|
14
|
+
v: 1;
|
|
15
|
+
kind: "runner.result";
|
|
16
|
+
runId: string;
|
|
17
|
+
testId: string;
|
|
18
|
+
title: string;
|
|
19
|
+
status: "passed" | "failed" | "skipped";
|
|
20
|
+
timestamp: string;
|
|
21
|
+
error?: {
|
|
22
|
+
name: string;
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** A Node-only, append-only sink; each Vitest worker owns its output file. */
|
|
27
|
+
export declare class NdjsonEventSink implements EventSink {
|
|
28
|
+
#private;
|
|
29
|
+
private readonly context;
|
|
30
|
+
constructor(context: ReportingContext);
|
|
31
|
+
emit(event: TestmintEvent): Promise<void>;
|
|
32
|
+
private recordFailure;
|
|
33
|
+
}
|
|
34
|
+
export declare function startManifest(context: ReportingContext): Promise<void>;
|
|
35
|
+
export declare function completeManifest(context: ReportingContext, status: "complete" | "incomplete"): Promise<void>;
|
|
36
|
+
export declare function readManifest(directory: string): Promise<RunManifest>;
|
|
37
|
+
export declare function appendRunnerResult(context: ReportingContext, result: Omit<RunnerResultEvent, "v" | "kind" | "runId" | "timestamp" | "error"> & {
|
|
38
|
+
error?: unknown;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=reporting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporting.d.ts","sourceRoot":"","sources":["../src/reporting.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC3B,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,YAAY,CAAC;IAC9C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC,CAAC,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAoBD,8EAA8E;AAC9E,qBAAa,eAAgB,YAAW,SAAS;;IAKpC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,gBAAgB,EAErD;IAEK,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9C;YAEa,aAAa;CAe3B;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5E;AAED,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,UAAU,GAAG,YAAY,GAC/B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAI1E;AAkBD,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,IAAI,CACX,iBAAiB,EACjB,GAAG,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CAC9C,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrB,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { threadId } from "node:worker_threads";
|
|
4
|
+
function producerFile(context) {
|
|
5
|
+
return resolve(context.outputDir, context.runId, "events", `producer-${process.pid}-${threadId}.ndjson`);
|
|
6
|
+
}
|
|
7
|
+
function producerFailureFile(context) {
|
|
8
|
+
return resolve(context.outputDir, context.runId, "events", `producer-${process.pid}-${threadId}.failure.json`);
|
|
9
|
+
}
|
|
10
|
+
/** A Node-only, append-only sink; each Vitest worker owns its output file. */
|
|
11
|
+
export class NdjsonEventSink {
|
|
12
|
+
context;
|
|
13
|
+
#file;
|
|
14
|
+
#ready;
|
|
15
|
+
#reportedFailure = false;
|
|
16
|
+
constructor(context) {
|
|
17
|
+
this.context = context;
|
|
18
|
+
this.#file = producerFile(context);
|
|
19
|
+
}
|
|
20
|
+
async emit(event) {
|
|
21
|
+
try {
|
|
22
|
+
this.#ready ??= mkdir(resolve(this.#file, ".."), {
|
|
23
|
+
recursive: true,
|
|
24
|
+
}).then(() => undefined);
|
|
25
|
+
await this.#ready;
|
|
26
|
+
await appendFile(this.#file, `${JSON.stringify(event)}\n`, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
await this.recordFailure(error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async recordFailure(error) {
|
|
34
|
+
if (this.#reportedFailure)
|
|
35
|
+
return;
|
|
36
|
+
this.#reportedFailure = true;
|
|
37
|
+
const detail = runnerError(error);
|
|
38
|
+
try {
|
|
39
|
+
await writeFile(producerFailureFile(this.context), JSON.stringify(detail), "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// The original I/O failure is reported below even when its marker cannot persist.
|
|
43
|
+
}
|
|
44
|
+
console.error("Testmints reporting could not write a phase event.", error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function startManifest(context) {
|
|
48
|
+
const directory = resolve(context.outputDir, context.runId);
|
|
49
|
+
await mkdir(directory, { recursive: true });
|
|
50
|
+
await writeFile(resolve(directory, "manifest.json"), JSON.stringify({
|
|
51
|
+
v: 1,
|
|
52
|
+
runId: context.runId,
|
|
53
|
+
status: "running",
|
|
54
|
+
producerFiles: [],
|
|
55
|
+
failureFiles: [],
|
|
56
|
+
}), "utf8");
|
|
57
|
+
}
|
|
58
|
+
export async function completeManifest(context, status) {
|
|
59
|
+
const directory = resolve(context.outputDir, context.runId);
|
|
60
|
+
const events = resolve(directory, "events");
|
|
61
|
+
let producerFiles = [];
|
|
62
|
+
let failureFiles = [];
|
|
63
|
+
try {
|
|
64
|
+
const { readdir } = await import("node:fs/promises");
|
|
65
|
+
const files = await readdir(events);
|
|
66
|
+
producerFiles = files.filter((file) => file.startsWith("producer-") && file.endsWith(".ndjson"));
|
|
67
|
+
failureFiles = files.filter((file) => file.startsWith("producer-") && file.endsWith(".failure.json"));
|
|
68
|
+
if (failureFiles.length > 0)
|
|
69
|
+
status = "incomplete";
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
status = "incomplete";
|
|
73
|
+
}
|
|
74
|
+
await writeFile(resolve(directory, "manifest.json"), JSON.stringify({
|
|
75
|
+
v: 1,
|
|
76
|
+
runId: context.runId,
|
|
77
|
+
status,
|
|
78
|
+
producerFiles,
|
|
79
|
+
failureFiles,
|
|
80
|
+
}), "utf8");
|
|
81
|
+
}
|
|
82
|
+
export async function readManifest(directory) {
|
|
83
|
+
return JSON.parse(await readFile(resolve(directory, "manifest.json"), "utf8"));
|
|
84
|
+
}
|
|
85
|
+
function runnerError(error) {
|
|
86
|
+
if (error instanceof Error)
|
|
87
|
+
return { name: error.name, message: error.message };
|
|
88
|
+
if (error !== null && typeof error === "object") {
|
|
89
|
+
const candidate = error;
|
|
90
|
+
return {
|
|
91
|
+
name: typeof candidate.name === "string" ? candidate.name : "Error",
|
|
92
|
+
message: typeof candidate.message === "string"
|
|
93
|
+
? candidate.message
|
|
94
|
+
: String(error),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return { name: "Error", message: String(error) };
|
|
98
|
+
}
|
|
99
|
+
export async function appendRunnerResult(context, result) {
|
|
100
|
+
const file = resolve(context.outputDir, context.runId, "events", "runner.ndjson");
|
|
101
|
+
await mkdir(resolve(file, ".."), { recursive: true });
|
|
102
|
+
await appendFile(file, `${JSON.stringify({
|
|
103
|
+
v: 1,
|
|
104
|
+
kind: "runner.result",
|
|
105
|
+
runId: context.runId,
|
|
106
|
+
testId: result.testId,
|
|
107
|
+
title: result.title,
|
|
108
|
+
status: result.status,
|
|
109
|
+
timestamp: new Date().toISOString(),
|
|
110
|
+
...(result.error === undefined
|
|
111
|
+
? {}
|
|
112
|
+
: { error: runnerError(result.error) }),
|
|
113
|
+
})}\n`, "utf8");
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=reporting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporting.js","sourceRoot":"","sources":["../src/reporting.ts"],"names":[],"mappings":"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"}
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { type TestmintDefinition } from "./core.js";
|
|
3
|
+
export interface TestmintsVitestOptions {
|
|
4
|
+
/** Root directory for run artifacts. Defaults to `.testmints`. */
|
|
5
|
+
outputDir?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Register a complete Testmint definition with Vitest. */
|
|
8
|
+
export declare function mintTest<Input, Output>(name: string, definition: TestmintDefinition<Input, Output>): void;
|
|
9
|
+
/** Enable local Testmints NDJSON artifacts for a Node Vitest project. */
|
|
10
|
+
export declare function testmintsVitest(options?: TestmintsVitestOptions): Plugin;
|
|
11
|
+
//# sourceMappingURL=vitest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,EAEN,KAAK,kBAAkB,EAEvB,MAAM,WAAW,CAAC;AASnB,MAAM,WAAW,sBAAsB;IACtC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAaD,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EACrC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,GAC3C,IAAI,CAsBN;AAyDD,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CA4B5E"}
|
package/dist/vitest.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { threadId } from "node:worker_threads";
|
|
2
|
+
import { inject, test } from "vitest";
|
|
3
|
+
import { executeDefinition, validateDefinition, } from "./core.js";
|
|
4
|
+
import { appendRunnerResult, completeManifest, NdjsonEventSink, startManifest, } from "./reporting.js";
|
|
5
|
+
function reportingContext() {
|
|
6
|
+
return inject("testmints");
|
|
7
|
+
}
|
|
8
|
+
function id() {
|
|
9
|
+
return crypto.randomUUID();
|
|
10
|
+
}
|
|
11
|
+
const producerId = `${process.pid}-${threadId}`;
|
|
12
|
+
let producerSequence = 0;
|
|
13
|
+
/** Register a complete Testmint definition with Vitest. */
|
|
14
|
+
export function mintTest(name, definition) {
|
|
15
|
+
const validated = validateDefinition(definition);
|
|
16
|
+
test(name, async (context) => {
|
|
17
|
+
const reporting = reportingContext();
|
|
18
|
+
const task = context.task;
|
|
19
|
+
await executeDefinition(validated, {
|
|
20
|
+
signal: context.signal,
|
|
21
|
+
...(reporting === undefined
|
|
22
|
+
? {}
|
|
23
|
+
: {
|
|
24
|
+
sink: new NdjsonEventSink(reporting),
|
|
25
|
+
eventContext: {
|
|
26
|
+
runId: reporting.runId,
|
|
27
|
+
producerId,
|
|
28
|
+
attemptId: id(),
|
|
29
|
+
testId: task.id,
|
|
30
|
+
title: task.fullName,
|
|
31
|
+
},
|
|
32
|
+
nextEventSequence: () => producerSequence++,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
class TestmintsReporter {
|
|
38
|
+
context;
|
|
39
|
+
#incomplete = false;
|
|
40
|
+
constructor(context) {
|
|
41
|
+
this.context = context;
|
|
42
|
+
}
|
|
43
|
+
async onTestRunStart() {
|
|
44
|
+
try {
|
|
45
|
+
await startManifest(this.context);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
this.#incomplete = true;
|
|
49
|
+
console.error("Testmints reporting could not create its artifact directory.", error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async onTestRunEnd() {
|
|
53
|
+
try {
|
|
54
|
+
await completeManifest(this.context, this.#incomplete ? "incomplete" : "complete");
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error("Testmints reporting could not finalize its artifacts.", error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async onTestCaseResult(testCase) {
|
|
61
|
+
const result = testCase.result();
|
|
62
|
+
if (result.state === "pending")
|
|
63
|
+
return;
|
|
64
|
+
try {
|
|
65
|
+
await appendRunnerResult(this.context, {
|
|
66
|
+
testId: testCase.id,
|
|
67
|
+
title: testCase.fullName,
|
|
68
|
+
status: result.state,
|
|
69
|
+
...(result.errors?.[0] === undefined
|
|
70
|
+
? {}
|
|
71
|
+
: { error: result.errors[0] }),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
this.#incomplete = true;
|
|
76
|
+
console.error("Testmints reporting could not record a Vitest outcome.", error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Enable local Testmints NDJSON artifacts for a Node Vitest project. */
|
|
81
|
+
export function testmintsVitest(options = {}) {
|
|
82
|
+
const outputDir = options.outputDir ?? ".testmints";
|
|
83
|
+
const context = {
|
|
84
|
+
outputDir,
|
|
85
|
+
runId: `run-${new Date().toISOString().replaceAll(":", "-")}-${id()}`,
|
|
86
|
+
};
|
|
87
|
+
const plugin = {
|
|
88
|
+
name: "testmints-vitest",
|
|
89
|
+
config: (config) => {
|
|
90
|
+
const configured = config.test?.reporters;
|
|
91
|
+
const reporters = configured === undefined
|
|
92
|
+
? []
|
|
93
|
+
: Array.isArray(configured)
|
|
94
|
+
? configured
|
|
95
|
+
: [configured];
|
|
96
|
+
return {
|
|
97
|
+
test: {
|
|
98
|
+
provide: { testmints: context },
|
|
99
|
+
reporters: [
|
|
100
|
+
...(reporters.length === 0 ? ["default"] : reporters),
|
|
101
|
+
new TestmintsReporter(context),
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
return plugin;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=vitest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@continuous-excellence/testmints-js",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Readable tests with explicit setup, exercise, verify, and teardown phases.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/robertfmurdock/testmints-js.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/robertfmurdock/testmints-js/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/robertfmurdock/testmints-js#readme",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"testmints": "./dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"packageManager": "npm@11.19.1",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22",
|
|
21
|
+
"npm": ">=11"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./vitest": {
|
|
29
|
+
"types": "./dist/vitest.d.ts",
|
|
30
|
+
"import": "./dist/vitest.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"vitest": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/types/tsconfig.json --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"lint": "biome check .",
|
|
48
|
+
"format": "biome check --write .",
|
|
49
|
+
"check": "npm run lint && npm run typecheck && npm run test",
|
|
50
|
+
"release:version": "bash scripts/release-version.sh",
|
|
51
|
+
"release:pack": "npm run build && node scripts/pack-release.mjs",
|
|
52
|
+
"release:verify": "node scripts/verify-release-package.mjs",
|
|
53
|
+
"release:tag": "bash scripts/tag-release.sh"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@biomejs/biome": "^2.5.14",
|
|
57
|
+
"@continuous-excellence/tagger": "3.6.18",
|
|
58
|
+
"@types/node": "^26.6.1",
|
|
59
|
+
"typescript": "^7.0.2",
|
|
60
|
+
"vitest": "^5.0.1"
|
|
61
|
+
}
|
|
62
|
+
}
|