@shivaedev/vitest-rerun 0.5.0 → 0.6.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/CHANGELOG.md +12 -0
- package/dist/adapters/VitestRunner.d.ts +8 -32
- package/dist/adapters/VitestRunner.d.ts.map +1 -1
- package/dist/adapters/VitestRunner.js +1 -58
- package/dist/adapters/VitestRunner.js.map +1 -1
- package/dist/adapters/open.d.ts +27 -0
- package/dist/adapters/open.d.ts.map +1 -0
- package/dist/adapters/open.js +60 -0
- package/dist/adapters/open.js.map +1 -0
- package/package.json +4 -4
- package/src/adapters/VitestRunner.ts +1 -87
- package/src/adapters/open.ts +89 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 - 2026-11-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Expose the existing Vitest runner `open` operation and its `RunOptions` type from `/adapters/open.ts`.
|
|
8
|
+
|
|
9
|
+
## 0.5.1 - 2026-11-04
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Update `@shivaedev/service` from 0.6.1 to 0.6.2.
|
|
14
|
+
|
|
3
15
|
## 0.5.0 - 2026-11-03
|
|
4
16
|
|
|
5
17
|
### Added
|
|
@@ -1,48 +1,24 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import
|
|
3
|
-
import type { Snapshot } from "#internal/snapshot.ts";
|
|
4
|
-
import type { Files } from "./tasks.ts";
|
|
5
|
-
interface RunOptions {
|
|
6
|
-
readonly cacheDir?: string;
|
|
7
|
-
readonly reports: {
|
|
8
|
-
readonly json: string;
|
|
9
|
-
readonly junit: string;
|
|
10
|
-
};
|
|
11
|
-
readonly rerun: boolean;
|
|
12
|
-
readonly vitestArgs: readonly string[];
|
|
13
|
-
}
|
|
2
|
+
import { open } from "./open.ts";
|
|
14
3
|
export interface Selection {
|
|
15
4
|
readonly fileId: string;
|
|
16
5
|
readonly testIds?: string[];
|
|
17
6
|
}
|
|
18
|
-
declare const open: (input: RunOptions) => Effect.Effect<{
|
|
19
|
-
collect: (files: Files, summary: Snapshot["summary"]) => Effect.Effect<void, never, never>;
|
|
20
|
-
finish: (outputDir: string, attempt: number, reports: RunOptions["reports"]) => Effect.Effect<void, import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("#error.ts").VitestRerunError, import("effect/FileSystem").FileSystem>;
|
|
21
|
-
read: () => Effect.Effect<{
|
|
22
|
-
errors: unknown[];
|
|
23
|
-
files: VitestNode.RunnerTestFile[];
|
|
24
|
-
root: string;
|
|
25
|
-
summary: import("vitest").SnapshotSummary;
|
|
26
|
-
}, never, never>;
|
|
27
|
-
rerun: (selected: readonly Selection[]) => Effect.Effect<undefined, import("#error.ts").VitestRerunError, never>;
|
|
28
|
-
start: () => Effect.Effect<void, import("#error.ts").VitestRerunError, never>;
|
|
29
|
-
}, import("#error.ts").VitestRerunError, import("effect/Scope").Scope>;
|
|
30
7
|
export declare const VitestRunner: import("effect/Context").Service<"@shivaedev/vitest-rerun/VitestRunner", Readonly<{
|
|
31
|
-
readonly open: (input: RunOptions) => Effect.Effect<{
|
|
32
|
-
collect: (files: Files, summary: Snapshot["summary"]) => Effect.Effect<void, never, never>;
|
|
33
|
-
finish: (outputDir: string, attempt: number, reports: RunOptions["reports"]) => Effect.Effect<void, import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("
|
|
8
|
+
readonly open: (input: import("./open.ts").RunOptions) => Effect.Effect<{
|
|
9
|
+
collect: (files: import("./tasks.ts").Files, summary: import("../internal/snapshot.ts").Snapshot["summary"]) => Effect.Effect<void, never, never>;
|
|
10
|
+
finish: (outputDir: string, attempt: number, reports: import("./open.ts").RunOptions["reports"]) => Effect.Effect<void, import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("../error.ts").VitestRerunError, import("effect/FileSystem").FileSystem>;
|
|
34
11
|
read: () => Effect.Effect<{
|
|
35
12
|
errors: unknown[];
|
|
36
|
-
files:
|
|
13
|
+
files: import("vitest").RunnerTestFile[];
|
|
37
14
|
root: string;
|
|
38
15
|
summary: import("vitest").SnapshotSummary;
|
|
39
16
|
}, never, never>;
|
|
40
|
-
rerun: (selected: readonly Selection[]) => Effect.Effect<undefined, import("
|
|
41
|
-
start: () => Effect.Effect<void, import("
|
|
42
|
-
}, import("
|
|
17
|
+
rerun: (selected: readonly Selection[]) => Effect.Effect<undefined, import("../error.ts").VitestRerunError, never>;
|
|
18
|
+
start: () => Effect.Effect<void, import("../error.ts").VitestRerunError, never>;
|
|
19
|
+
}, import("../error.ts").VitestRerunError, import("effect/Scope").Scope>;
|
|
43
20
|
}>> & {
|
|
44
21
|
readonly layer: import("effect/Layer").Layer<"@shivaedev/vitest-rerun/VitestRunner", never, never>;
|
|
45
22
|
};
|
|
46
23
|
export type AttemptRunner = Effect.Success<ReturnType<typeof open>>;
|
|
47
|
-
export {};
|
|
48
24
|
//# sourceMappingURL=VitestRunner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VitestRunner.d.ts","sourceRoot":"","sources":["../../src/adapters/VitestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"VitestRunner.d.ts","sourceRoot":"","sources":["../../src/adapters/VitestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;CAKvB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC"}
|
|
@@ -1,63 +1,6 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import * as VitestNode from "vitest/node";
|
|
3
2
|
import { defineService } from "@shivaedev/service/defineService.ts";
|
|
4
|
-
import {
|
|
5
|
-
import { deferCoverageThresholds } from "./deferCoverageThresholds.js";
|
|
6
|
-
import { mergeSnapshotSummary } from "./mergeSnapshotSummary.js";
|
|
7
|
-
import { RerunJunitReporter } from "./RerunJunitReporter.js";
|
|
8
|
-
import { TimedJsonReporter } from "./TimedJsonReporter.js";
|
|
9
|
-
import { writeMergedReports } from "./writeMergedReports.js";
|
|
10
|
-
function promise(message, run) {
|
|
11
|
-
return Effect.tryPromise({ catch: failWith(message), try: run });
|
|
12
|
-
}
|
|
13
|
-
const open = Effect.fn("VitestRerun.open")(function* (input) {
|
|
14
|
-
const { filter, options } = yield* Effect.try({
|
|
15
|
-
catch: (cause) => failWith(`Could not parse Vitest arguments: ${cause instanceof Error ? cause.message : String(cause)}`)(cause),
|
|
16
|
-
try: () => VitestNode.parseCLI(["vitest", "run", ...input.vitestArgs]),
|
|
17
|
-
});
|
|
18
|
-
const { shard: _shard, ...unsharded } = options;
|
|
19
|
-
const junit = new RerunJunitReporter(input.reports.junit);
|
|
20
|
-
const vitest = yield* Effect.acquireRelease(promise("Vitest could not start", () => VitestNode.createVitest({
|
|
21
|
-
...(input.rerun ? unsharded : options),
|
|
22
|
-
reporters: ["default", new TimedJsonReporter({ outputFile: input.reports.json }), junit],
|
|
23
|
-
retry: 0,
|
|
24
|
-
watch: false,
|
|
25
|
-
}, input.cacheDir === undefined ? {} : { cacheDir: input.cacheDir })), (instance) => Effect.promise(() => instance.close()));
|
|
26
|
-
const finishCoverage = yield* deferCoverageThresholds(vitest);
|
|
27
|
-
return {
|
|
28
|
-
collect: (files, summary) => Effect.sync(() => {
|
|
29
|
-
for (const file of files) {
|
|
30
|
-
vitest.state.collectFiles(vitest.getProjectByName(file.projectName ?? ""), [file]);
|
|
31
|
-
}
|
|
32
|
-
vitest.snapshot.summary = mergeSnapshotSummary(summary, vitest.snapshot.summary);
|
|
33
|
-
}),
|
|
34
|
-
finish: (outputDir, attempt, reports) => Effect.gen(function* () {
|
|
35
|
-
const coverage = yield* finishCoverage(outputDir, attempt, input.reports.json);
|
|
36
|
-
yield* writeMergedReports(vitest, reports, coverage, junit.unhandledErrors);
|
|
37
|
-
}),
|
|
38
|
-
read: () => Effect.sync(() => ({
|
|
39
|
-
errors: vitest.state.getUnhandledErrors(),
|
|
40
|
-
files: vitest.state.getFiles(),
|
|
41
|
-
root: vitest.config.root,
|
|
42
|
-
summary: vitest.snapshot.summary,
|
|
43
|
-
})),
|
|
44
|
-
rerun: (selected) => Effect.gen(function* () {
|
|
45
|
-
yield* promise("Vitest could not prepare the rerun", () => vitest.standalone());
|
|
46
|
-
const specifications = yield* promise("Vitest could not list test files", () => vitest.globTestSpecifications());
|
|
47
|
-
const failed = specifications.flatMap((spec) => {
|
|
48
|
-
const selection = selected.find((item) => item.fileId === spec.taskId);
|
|
49
|
-
return selection === undefined
|
|
50
|
-
? []
|
|
51
|
-
: [spec.project.createSpecification(spec.moduleId, selection.testIds === undefined ? {} : { testIds: selection.testIds }, spec.pool)];
|
|
52
|
-
});
|
|
53
|
-
if (failed.length === 0) {
|
|
54
|
-
return yield* Effect.fail(failWith("No failed specifications found for the fresh rerun")(undefined));
|
|
55
|
-
}
|
|
56
|
-
yield* promise("Vitest could not rerun the failed tests", () => vitest.runTestSpecifications(failed));
|
|
57
|
-
}),
|
|
58
|
-
start: () => promise("Vitest could not run the tests", () => vitest.start(filter)).pipe(Effect.catchTag("VitestRerunError", (error) => vitest.config.passWithNoTests && error.cause instanceof VitestNode.TestsNotFoundError ? Effect.void : Effect.fail(error)), Effect.asVoid),
|
|
59
|
-
};
|
|
60
|
-
});
|
|
3
|
+
import { open } from "./open.js";
|
|
61
4
|
export const VitestRunner = defineService({
|
|
62
5
|
id: "@shivaedev/vitest-rerun/VitestRunner",
|
|
63
6
|
initialize: Effect.void,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VitestRunner.js","sourceRoot":"","sources":["../../src/adapters/VitestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"VitestRunner.js","sourceRoot":"","sources":["../../src/adapters/VitestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;IACzC,EAAE,EAAE,sCAAsC;IAC1C,UAAU,EAAE,MAAM,CAAC,IAAI;IACvB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACzB,QAAQ,EAAE,EAAE;CACZ,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as VitestNode from "vitest/node";
|
|
3
|
+
import type { Snapshot } from "#internal/snapshot.ts";
|
|
4
|
+
import type { Files } from "./tasks.ts";
|
|
5
|
+
import type { Selection } from "./VitestRunner.ts";
|
|
6
|
+
export interface RunOptions {
|
|
7
|
+
readonly cacheDir?: string;
|
|
8
|
+
readonly reports: {
|
|
9
|
+
readonly json: string;
|
|
10
|
+
readonly junit: string;
|
|
11
|
+
};
|
|
12
|
+
readonly rerun: boolean;
|
|
13
|
+
readonly vitestArgs: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare const open: (input: RunOptions) => Effect.Effect<{
|
|
16
|
+
collect: (files: Files, summary: Snapshot["summary"]) => Effect.Effect<void, never, never>;
|
|
17
|
+
finish: (outputDir: string, attempt: number, reports: RunOptions["reports"]) => Effect.Effect<void, import("effect/PlatformError").PlatformError | import("effect/Schema").SchemaError | import("#error.ts").VitestRerunError, import("effect/FileSystem").FileSystem>;
|
|
18
|
+
read: () => Effect.Effect<{
|
|
19
|
+
errors: unknown[];
|
|
20
|
+
files: VitestNode.RunnerTestFile[];
|
|
21
|
+
root: string;
|
|
22
|
+
summary: import("vitest").SnapshotSummary;
|
|
23
|
+
}, never, never>;
|
|
24
|
+
rerun: (selected: readonly Selection[]) => Effect.Effect<undefined, import("#error.ts").VitestRerunError, never>;
|
|
25
|
+
start: () => Effect.Effect<void, import("#error.ts").VitestRerunError, never>;
|
|
26
|
+
}, import("#error.ts").VitestRerunError, import("effect/Scope").Scope>;
|
|
27
|
+
//# sourceMappingURL=open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/adapters/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAMD,eAAO,MAAM,IAAI;qBAuBE,KAAK,WAAW,QAAQ,CAAC,SAAS,CAAC;wBAOhC,MAAM,WAAW,MAAM,WAAW,UAAU,CAAC,SAAS,CAAC;;;;;;;sBAYzD,SAAS,SAAS,EAAE;;sEAuBtC,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as VitestNode from "vitest/node";
|
|
3
|
+
import { failWith } from "#error.ts";
|
|
4
|
+
import { deferCoverageThresholds } from "./deferCoverageThresholds.js";
|
|
5
|
+
import { mergeSnapshotSummary } from "./mergeSnapshotSummary.js";
|
|
6
|
+
import { RerunJunitReporter } from "./RerunJunitReporter.js";
|
|
7
|
+
import { TimedJsonReporter } from "./TimedJsonReporter.js";
|
|
8
|
+
import { writeMergedReports } from "./writeMergedReports.js";
|
|
9
|
+
function promise(message, run) {
|
|
10
|
+
return Effect.tryPromise({ catch: failWith(message), try: run });
|
|
11
|
+
}
|
|
12
|
+
export const open = Effect.fn("VitestRerun.open")(function* (input) {
|
|
13
|
+
const { filter, options } = yield* Effect.try({
|
|
14
|
+
catch: (cause) => failWith(`Could not parse Vitest arguments: ${cause instanceof Error ? cause.message : String(cause)}`)(cause),
|
|
15
|
+
try: () => VitestNode.parseCLI(["vitest", "run", ...input.vitestArgs]),
|
|
16
|
+
});
|
|
17
|
+
const { shard: _shard, ...unsharded } = options;
|
|
18
|
+
const junit = new RerunJunitReporter(input.reports.junit);
|
|
19
|
+
const vitest = yield* Effect.acquireRelease(promise("Vitest could not start", () => VitestNode.createVitest({
|
|
20
|
+
...(input.rerun ? unsharded : options),
|
|
21
|
+
reporters: ["default", new TimedJsonReporter({ outputFile: input.reports.json }), junit],
|
|
22
|
+
retry: 0,
|
|
23
|
+
watch: false,
|
|
24
|
+
}, input.cacheDir === undefined ? {} : { cacheDir: input.cacheDir })), (instance) => Effect.promise(() => instance.close()));
|
|
25
|
+
const finishCoverage = yield* deferCoverageThresholds(vitest);
|
|
26
|
+
return {
|
|
27
|
+
collect: (files, summary) => Effect.sync(() => {
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
vitest.state.collectFiles(vitest.getProjectByName(file.projectName ?? ""), [file]);
|
|
30
|
+
}
|
|
31
|
+
vitest.snapshot.summary = mergeSnapshotSummary(summary, vitest.snapshot.summary);
|
|
32
|
+
}),
|
|
33
|
+
finish: (outputDir, attempt, reports) => Effect.gen(function* () {
|
|
34
|
+
const coverage = yield* finishCoverage(outputDir, attempt, input.reports.json);
|
|
35
|
+
yield* writeMergedReports(vitest, reports, coverage, junit.unhandledErrors);
|
|
36
|
+
}),
|
|
37
|
+
read: () => Effect.sync(() => ({
|
|
38
|
+
errors: vitest.state.getUnhandledErrors(),
|
|
39
|
+
files: vitest.state.getFiles(),
|
|
40
|
+
root: vitest.config.root,
|
|
41
|
+
summary: vitest.snapshot.summary,
|
|
42
|
+
})),
|
|
43
|
+
rerun: (selected) => Effect.gen(function* () {
|
|
44
|
+
yield* promise("Vitest could not prepare the rerun", () => vitest.standalone());
|
|
45
|
+
const specifications = yield* promise("Vitest could not list test files", () => vitest.globTestSpecifications());
|
|
46
|
+
const failed = specifications.flatMap((spec) => {
|
|
47
|
+
const selection = selected.find((item) => item.fileId === spec.taskId);
|
|
48
|
+
return selection === undefined
|
|
49
|
+
? []
|
|
50
|
+
: [spec.project.createSpecification(spec.moduleId, selection.testIds === undefined ? {} : { testIds: selection.testIds }, spec.pool)];
|
|
51
|
+
});
|
|
52
|
+
if (failed.length === 0) {
|
|
53
|
+
return yield* Effect.fail(failWith("No failed specifications found for the fresh rerun")(undefined));
|
|
54
|
+
}
|
|
55
|
+
yield* promise("Vitest could not rerun the failed tests", () => vitest.runTestSpecifications(failed));
|
|
56
|
+
}),
|
|
57
|
+
start: () => promise("Vitest could not run the tests", () => vitest.start(filter)).pipe(Effect.catchTag("VitestRerunError", (error) => vitest.config.passWithNoTests && error.cause instanceof VitestNode.TestsNotFoundError ? Effect.void : Effect.fail(error)), Effect.asVoid),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/adapters/open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAS7D,SAAS,OAAO,CAAI,OAAe,EAAE,GAAqB;IACzD,OAAO,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAiB;IAC7E,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAC7C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,qCAAqC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QAChI,GAAG,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;KACtE,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,OAAO,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC1C,OAAO,CAAC,wBAAwB,EAAE,GAAG,EAAE,CACtC,UAAU,CAAC,YAAY,CACtB;QACC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QACtC,SAAS,EAAE,CAAC,SAAS,EAAE,IAAI,iBAAiB,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC;QACxF,KAAK,EAAE,CAAC;QACR,KAAK,EAAE,KAAK;KACZ,EACD,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAChE,CACD,EACD,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CACpD,CAAC;IACF,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAC9D,OAAO;QACN,OAAO,EAAE,CAAC,KAAY,EAAE,OAA4B,EAAE,EAAE,CACvD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAC1B,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YACpF,CAAC;YACD,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClF,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,SAAiB,EAAE,OAAe,EAAE,OAA8B,EAAE,EAAE,CAC9E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC/E,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;QAC7E,CAAC,CAAC;QACH,IAAI,EAAE,GAAG,EAAE,CACV,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE;YACzC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;YACxB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO;SAChC,CAAC,CAAC;QACJ,KAAK,EAAE,CAAC,QAA8B,EAAE,EAAE,CACzC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,KAAK,CAAC,CAAC,OAAO,CAAC,oCAAoC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;YAChF,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,kCAAkC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,sBAAsB,EAAE,CAAC,CAAC;YACjH,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC9C,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvE,OAAO,SAAS,KAAK,SAAS;oBAC7B,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACxI,CAAC,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,oDAAoD,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;YACtG,CAAC;YACD,KAAK,CAAC,CAAC,OAAO,CAAC,yCAAyC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;QACvG,CAAC,CAAC;QACH,KAAK,EAAE,GAAG,EAAE,CACX,OAAO,CAAC,gCAAgC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CACzE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC7C,MAAM,CAAC,MAAM,CAAC,eAAe,IAAI,KAAK,CAAC,KAAK,YAAY,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CACxH,EACD,MAAM,CAAC,MAAM,CACb;KACF,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/vitest-rerun",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Reruns failed Vitest tests in a fresh process and reports which tests were flaky",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/vitest-rerun#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@shivaedev/service": "0.6.
|
|
46
|
+
"@shivaedev/service": "0.6.2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@effect/platform-node": "4.0.6",
|
|
50
50
|
"@effect/platform-node-shared": "4.0.6",
|
|
51
51
|
"@effect/vitest": "4.0.6",
|
|
52
|
-
"@shivaedev/quality": "0.
|
|
52
|
+
"@shivaedev/quality": "0.59.0",
|
|
53
53
|
"@shivaedev/test": "0.14.0",
|
|
54
|
-
"@shivaedev/test-story": "0.
|
|
54
|
+
"@shivaedev/test-story": "0.37.1",
|
|
55
55
|
"@types/node": "24.19.2",
|
|
56
56
|
"@vitest/coverage-v8": "5.0.5",
|
|
57
57
|
"effect": "4.0.6",
|
|
@@ -1,98 +1,12 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import * as VitestNode from "vitest/node";
|
|
3
2
|
import { defineService } from "@shivaedev/service/defineService.ts";
|
|
4
|
-
import {
|
|
5
|
-
import type { Snapshot } from "#internal/snapshot.ts";
|
|
6
|
-
import { deferCoverageThresholds } from "./deferCoverageThresholds.ts";
|
|
7
|
-
import { mergeSnapshotSummary } from "./mergeSnapshotSummary.ts";
|
|
8
|
-
import { RerunJunitReporter } from "./RerunJunitReporter.ts";
|
|
9
|
-
import { TimedJsonReporter } from "./TimedJsonReporter.ts";
|
|
10
|
-
import type { Files } from "./tasks.ts";
|
|
11
|
-
import { writeMergedReports } from "./writeMergedReports.ts";
|
|
12
|
-
|
|
13
|
-
interface RunOptions {
|
|
14
|
-
readonly cacheDir?: string;
|
|
15
|
-
readonly reports: { readonly json: string; readonly junit: string };
|
|
16
|
-
readonly rerun: boolean;
|
|
17
|
-
readonly vitestArgs: readonly string[];
|
|
18
|
-
}
|
|
3
|
+
import { open } from "./open.ts";
|
|
19
4
|
|
|
20
5
|
export interface Selection {
|
|
21
6
|
readonly fileId: string;
|
|
22
7
|
readonly testIds?: string[];
|
|
23
8
|
}
|
|
24
9
|
|
|
25
|
-
function promise<A>(message: string, run: () => Promise<A>) {
|
|
26
|
-
return Effect.tryPromise({ catch: failWith(message), try: run });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const open = Effect.fn("VitestRerun.open")(function* (input: RunOptions) {
|
|
30
|
-
const { filter, options } = yield* Effect.try({
|
|
31
|
-
catch: (cause) => failWith(`Could not parse Vitest arguments: ${cause instanceof Error ? cause.message : String(cause)}`)(cause),
|
|
32
|
-
try: () => VitestNode.parseCLI(["vitest", "run", ...input.vitestArgs]),
|
|
33
|
-
});
|
|
34
|
-
const { shard: _shard, ...unsharded } = options;
|
|
35
|
-
const junit = new RerunJunitReporter(input.reports.junit);
|
|
36
|
-
const vitest = yield* Effect.acquireRelease(
|
|
37
|
-
promise("Vitest could not start", () =>
|
|
38
|
-
VitestNode.createVitest(
|
|
39
|
-
{
|
|
40
|
-
...(input.rerun ? unsharded : options),
|
|
41
|
-
reporters: ["default", new TimedJsonReporter({ outputFile: input.reports.json }), junit],
|
|
42
|
-
retry: 0,
|
|
43
|
-
watch: false,
|
|
44
|
-
},
|
|
45
|
-
input.cacheDir === undefined ? {} : { cacheDir: input.cacheDir },
|
|
46
|
-
),
|
|
47
|
-
),
|
|
48
|
-
(instance) => Effect.promise(() => instance.close()),
|
|
49
|
-
);
|
|
50
|
-
const finishCoverage = yield* deferCoverageThresholds(vitest);
|
|
51
|
-
return {
|
|
52
|
-
collect: (files: Files, summary: Snapshot["summary"]) =>
|
|
53
|
-
Effect.sync(() => {
|
|
54
|
-
for (const file of files) {
|
|
55
|
-
vitest.state.collectFiles(vitest.getProjectByName(file.projectName ?? ""), [file]);
|
|
56
|
-
}
|
|
57
|
-
vitest.snapshot.summary = mergeSnapshotSummary(summary, vitest.snapshot.summary);
|
|
58
|
-
}),
|
|
59
|
-
finish: (outputDir: string, attempt: number, reports: RunOptions["reports"]) =>
|
|
60
|
-
Effect.gen(function* () {
|
|
61
|
-
const coverage = yield* finishCoverage(outputDir, attempt, input.reports.json);
|
|
62
|
-
yield* writeMergedReports(vitest, reports, coverage, junit.unhandledErrors);
|
|
63
|
-
}),
|
|
64
|
-
read: () =>
|
|
65
|
-
Effect.sync(() => ({
|
|
66
|
-
errors: vitest.state.getUnhandledErrors(),
|
|
67
|
-
files: vitest.state.getFiles(),
|
|
68
|
-
root: vitest.config.root,
|
|
69
|
-
summary: vitest.snapshot.summary,
|
|
70
|
-
})),
|
|
71
|
-
rerun: (selected: readonly Selection[]) =>
|
|
72
|
-
Effect.gen(function* () {
|
|
73
|
-
yield* promise("Vitest could not prepare the rerun", () => vitest.standalone());
|
|
74
|
-
const specifications = yield* promise("Vitest could not list test files", () => vitest.globTestSpecifications());
|
|
75
|
-
const failed = specifications.flatMap((spec) => {
|
|
76
|
-
const selection = selected.find((item) => item.fileId === spec.taskId);
|
|
77
|
-
return selection === undefined
|
|
78
|
-
? []
|
|
79
|
-
: [spec.project.createSpecification(spec.moduleId, selection.testIds === undefined ? {} : { testIds: selection.testIds }, spec.pool)];
|
|
80
|
-
});
|
|
81
|
-
if (failed.length === 0) {
|
|
82
|
-
return yield* Effect.fail(failWith("No failed specifications found for the fresh rerun")(undefined));
|
|
83
|
-
}
|
|
84
|
-
yield* promise("Vitest could not rerun the failed tests", () => vitest.runTestSpecifications(failed));
|
|
85
|
-
}),
|
|
86
|
-
start: () =>
|
|
87
|
-
promise("Vitest could not run the tests", () => vitest.start(filter)).pipe(
|
|
88
|
-
Effect.catchTag("VitestRerunError", (error) =>
|
|
89
|
-
vitest.config.passWithNoTests && error.cause instanceof VitestNode.TestsNotFoundError ? Effect.void : Effect.fail(error),
|
|
90
|
-
),
|
|
91
|
-
Effect.asVoid,
|
|
92
|
-
),
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
|
|
96
10
|
export const VitestRunner = defineService({
|
|
97
11
|
id: "@shivaedev/vitest-rerun/VitestRunner",
|
|
98
12
|
initialize: Effect.void,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as VitestNode from "vitest/node";
|
|
3
|
+
import { failWith } from "#error.ts";
|
|
4
|
+
import type { Snapshot } from "#internal/snapshot.ts";
|
|
5
|
+
import { deferCoverageThresholds } from "./deferCoverageThresholds.ts";
|
|
6
|
+
import { mergeSnapshotSummary } from "./mergeSnapshotSummary.ts";
|
|
7
|
+
import { RerunJunitReporter } from "./RerunJunitReporter.ts";
|
|
8
|
+
import { TimedJsonReporter } from "./TimedJsonReporter.ts";
|
|
9
|
+
import type { Files } from "./tasks.ts";
|
|
10
|
+
import type { Selection } from "./VitestRunner.ts";
|
|
11
|
+
import { writeMergedReports } from "./writeMergedReports.ts";
|
|
12
|
+
|
|
13
|
+
export interface RunOptions {
|
|
14
|
+
readonly cacheDir?: string;
|
|
15
|
+
readonly reports: { readonly json: string; readonly junit: string };
|
|
16
|
+
readonly rerun: boolean;
|
|
17
|
+
readonly vitestArgs: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function promise<A>(message: string, run: () => Promise<A>) {
|
|
21
|
+
return Effect.tryPromise({ catch: failWith(message), try: run });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const open = Effect.fn("VitestRerun.open")(function* (input: RunOptions) {
|
|
25
|
+
const { filter, options } = yield* Effect.try({
|
|
26
|
+
catch: (cause) => failWith(`Could not parse Vitest arguments: ${cause instanceof Error ? cause.message : String(cause)}`)(cause),
|
|
27
|
+
try: () => VitestNode.parseCLI(["vitest", "run", ...input.vitestArgs]),
|
|
28
|
+
});
|
|
29
|
+
const { shard: _shard, ...unsharded } = options;
|
|
30
|
+
const junit = new RerunJunitReporter(input.reports.junit);
|
|
31
|
+
const vitest = yield* Effect.acquireRelease(
|
|
32
|
+
promise("Vitest could not start", () =>
|
|
33
|
+
VitestNode.createVitest(
|
|
34
|
+
{
|
|
35
|
+
...(input.rerun ? unsharded : options),
|
|
36
|
+
reporters: ["default", new TimedJsonReporter({ outputFile: input.reports.json }), junit],
|
|
37
|
+
retry: 0,
|
|
38
|
+
watch: false,
|
|
39
|
+
},
|
|
40
|
+
input.cacheDir === undefined ? {} : { cacheDir: input.cacheDir },
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
(instance) => Effect.promise(() => instance.close()),
|
|
44
|
+
);
|
|
45
|
+
const finishCoverage = yield* deferCoverageThresholds(vitest);
|
|
46
|
+
return {
|
|
47
|
+
collect: (files: Files, summary: Snapshot["summary"]) =>
|
|
48
|
+
Effect.sync(() => {
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
vitest.state.collectFiles(vitest.getProjectByName(file.projectName ?? ""), [file]);
|
|
51
|
+
}
|
|
52
|
+
vitest.snapshot.summary = mergeSnapshotSummary(summary, vitest.snapshot.summary);
|
|
53
|
+
}),
|
|
54
|
+
finish: (outputDir: string, attempt: number, reports: RunOptions["reports"]) =>
|
|
55
|
+
Effect.gen(function* () {
|
|
56
|
+
const coverage = yield* finishCoverage(outputDir, attempt, input.reports.json);
|
|
57
|
+
yield* writeMergedReports(vitest, reports, coverage, junit.unhandledErrors);
|
|
58
|
+
}),
|
|
59
|
+
read: () =>
|
|
60
|
+
Effect.sync(() => ({
|
|
61
|
+
errors: vitest.state.getUnhandledErrors(),
|
|
62
|
+
files: vitest.state.getFiles(),
|
|
63
|
+
root: vitest.config.root,
|
|
64
|
+
summary: vitest.snapshot.summary,
|
|
65
|
+
})),
|
|
66
|
+
rerun: (selected: readonly Selection[]) =>
|
|
67
|
+
Effect.gen(function* () {
|
|
68
|
+
yield* promise("Vitest could not prepare the rerun", () => vitest.standalone());
|
|
69
|
+
const specifications = yield* promise("Vitest could not list test files", () => vitest.globTestSpecifications());
|
|
70
|
+
const failed = specifications.flatMap((spec) => {
|
|
71
|
+
const selection = selected.find((item) => item.fileId === spec.taskId);
|
|
72
|
+
return selection === undefined
|
|
73
|
+
? []
|
|
74
|
+
: [spec.project.createSpecification(spec.moduleId, selection.testIds === undefined ? {} : { testIds: selection.testIds }, spec.pool)];
|
|
75
|
+
});
|
|
76
|
+
if (failed.length === 0) {
|
|
77
|
+
return yield* Effect.fail(failWith("No failed specifications found for the fresh rerun")(undefined));
|
|
78
|
+
}
|
|
79
|
+
yield* promise("Vitest could not rerun the failed tests", () => vitest.runTestSpecifications(failed));
|
|
80
|
+
}),
|
|
81
|
+
start: () =>
|
|
82
|
+
promise("Vitest could not run the tests", () => vitest.start(filter)).pipe(
|
|
83
|
+
Effect.catchTag("VitestRerunError", (error) =>
|
|
84
|
+
vitest.config.passWithNoTests && error.cause instanceof VitestNode.TestsNotFoundError ? Effect.void : Effect.fail(error),
|
|
85
|
+
),
|
|
86
|
+
Effect.asVoid,
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
});
|