@systemfsoftware/stryker-js-vitest-runner 4.0.4 → 6.0.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 +67 -0
- package/README.md +59 -16
- package/dist/execAsync-CUNMu62l.mjs +10 -0
- package/dist/execAsync-zBkwnfr9.mjs +10 -0
- package/dist/getMachineId-bsd-Ci7jvBhd.mjs +26 -0
- package/dist/getMachineId-bsd-DEwG9Ywj.mjs +26 -0
- package/dist/getMachineId-darwin-K9sCO4zL.mjs +23 -0
- package/dist/getMachineId-darwin-s7YULPyU.mjs +23 -0
- package/dist/getMachineId-linux-B4KmiJGj.mjs +19 -0
- package/dist/getMachineId-linux-NPalN4Yq.mjs +19 -0
- package/dist/getMachineId-unsupported-DpGC7koj.mjs +14 -0
- package/dist/getMachineId-unsupported-Dqj1BWmV.mjs +14 -0
- package/dist/getMachineId-win-B9REJgj2.mjs +25 -0
- package/dist/getMachineId-win-tvSIkbRI.mjs +25 -0
- package/dist/index.d.mts +5 -18
- package/dist/index.mjs +6 -1034
- package/dist/main.d.mts +1 -0
- package/dist/main.mjs +51831 -0
- package/dist/stryker-setup.d.mts +1 -1
- package/package.json +65 -22
package/dist/index.mjs
CHANGED
|
@@ -1,1036 +1,8 @@
|
|
|
1
|
-
import { INSTRUMENTER_CONSTANTS, Module, RunConfiguration, SandboxDirectory, TestRunner, TestRunnerFailed, declarePlugin, errorToString, normalizeFileName, testFilesProvided } from "@systemfsoftware/stryker-js";
|
|
2
|
-
import * as Effect$1 from "effect/Effect";
|
|
3
|
-
import * as Layer from "effect/Layer";
|
|
4
|
-
import * as S from "effect/Schema";
|
|
5
|
-
import { createVitest } from "vitest/node";
|
|
6
|
-
import { Cell, Workflow } from "@systemfsoftware/effect-cell-types";
|
|
7
|
-
import * as Context from "effect/Context";
|
|
8
|
-
import * as FileSystem from "effect/FileSystem";
|
|
9
|
-
import * as Match from "effect/Match";
|
|
10
|
-
import * as Option from "effect/Option";
|
|
11
|
-
import * as Path from "effect/Path";
|
|
12
|
-
import * as Predicate from "effect/Predicate";
|
|
13
|
-
import * as Ref from "effect/Ref";
|
|
14
|
-
import * as Result from "effect/Result";
|
|
15
|
-
import { Effect } from "effect";
|
|
16
|
-
//#region src/interpret-vitest-run.workflow.ts
|
|
17
|
-
var VitestMutantRunCommand = class extends S.TaggedClass()("VitestMutantRunCommand", {
|
|
18
|
-
rawTests: S.Array(S.Unknown),
|
|
19
|
-
projectRoot: S.String,
|
|
20
|
-
hasExternalError: S.Boolean,
|
|
21
|
-
externalErrorText: S.String,
|
|
22
|
-
hitCount: S.optional(S.Finite),
|
|
23
|
-
hitLimit: S.optional(S.Finite),
|
|
24
|
-
reportAllKillers: S.Boolean
|
|
25
|
-
}) {};
|
|
26
|
-
const VitestMutantRunTypeId = Symbol.for("@systemfsoftware/stryker-js-vitest-runner/VitestMutantRun");
|
|
27
|
-
var MutantKilled = class extends S.TaggedClass()("Killed", {
|
|
28
|
-
testsJson: S.String,
|
|
29
|
-
killerIds: S.optional(S.Array(S.String)),
|
|
30
|
-
failureMessage: S.optional(S.String)
|
|
31
|
-
}) {
|
|
32
|
-
[VitestMutantRunTypeId] = VitestMutantRunTypeId;
|
|
33
|
-
};
|
|
34
|
-
var MutantSurvived = class extends S.TaggedClass()("Survived", { testsJson: S.String }) {
|
|
35
|
-
[VitestMutantRunTypeId] = VitestMutantRunTypeId;
|
|
36
|
-
};
|
|
37
|
-
var MutantTimeout = class extends S.TaggedClass()("Timeout", {
|
|
38
|
-
testsJson: S.String,
|
|
39
|
-
reason: S.optional(S.String)
|
|
40
|
-
}) {
|
|
41
|
-
[VitestMutantRunTypeId] = VitestMutantRunTypeId;
|
|
42
|
-
};
|
|
43
|
-
var MutantDryError = class extends S.TaggedClass()("Error", {
|
|
44
|
-
testsJson: S.String,
|
|
45
|
-
errorMessage: S.optional(S.String)
|
|
46
|
-
}) {
|
|
47
|
-
[VitestMutantRunTypeId] = VitestMutantRunTypeId;
|
|
48
|
-
};
|
|
49
|
-
var VitestMutantRunError = class extends S.TaggedError()("VitestMutantRunError", { message: S.String }) {
|
|
50
|
-
[VitestMutantRunTypeId] = VitestMutantRunTypeId;
|
|
51
|
-
};
|
|
52
|
-
var VitestDryRunCommand$1 = class extends S.TaggedClass()("VitestDryRunCommand", {
|
|
53
|
-
rawTests: S.Array(S.Unknown),
|
|
54
|
-
projectRoot: S.String,
|
|
55
|
-
hasExternalError: S.Boolean,
|
|
56
|
-
externalErrorText: S.String
|
|
57
|
-
}) {};
|
|
58
|
-
var VitestDryRunOutput = class extends S.TaggedClass()("VitestDryRunOutput", {
|
|
59
|
-
status: S.Literals(["Complete", "Error"]),
|
|
60
|
-
testsJson: S.String,
|
|
61
|
-
errorMessage: S.optional(S.String)
|
|
62
|
-
}) {};
|
|
63
|
-
const recordOption$1 = (value) => S.decodeUnknownOption(S.Record(S.String, S.Unknown))(value);
|
|
64
|
-
const getStringField$1 = (record, key) => Option.fromNullishOr(record[key]).pipe(Option.filter((v) => typeof v === "string"));
|
|
65
|
-
const getNumberField$1 = (record, key) => Option.fromNullishOr(record[key]).pipe(Option.filter((v) => typeof v === "number"));
|
|
66
|
-
const getSuite$1 = (value) => recordOption$1(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["suite"])));
|
|
67
|
-
const getFile$1 = (value) => recordOption$1(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["file"])));
|
|
68
|
-
const getResult$1 = (value) => recordOption$1(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["result"])));
|
|
69
|
-
const getErrors$1 = (value) => recordOption$1(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["errors"])), Option.filter((v) => Array.isArray(v)));
|
|
70
|
-
const getMessage$1 = (value) => recordOption$1(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["message"])), Option.filter((v) => typeof v === "string"));
|
|
71
|
-
const getName$1 = (value) => Option.match(recordOption$1(value), {
|
|
72
|
-
onNone: () => "",
|
|
73
|
-
onSome: (rec) => Option.getOrElse(getStringField$1(rec, "name"), () => "")
|
|
74
|
-
});
|
|
75
|
-
const getMode$1 = (value) => Option.match(recordOption$1(value), {
|
|
76
|
-
onNone: () => "run",
|
|
77
|
-
onSome: (rec) => Option.getOrElse(getStringField$1(rec, "mode"), () => "run")
|
|
78
|
-
});
|
|
79
|
-
const getState$1 = (value) => Match.value(value).pipe(Match.when("pass", () => "pass"), Match.when("fail", () => "fail"), Match.when("skip", () => "skip"), Match.when("todo", () => "todo"), Match.when("run", () => "run"), Match.when("queued", () => "queued"), Match.when("only", () => "only"), Match.when(void 0, () => void 0), Match.orElse(() => void 0));
|
|
80
|
-
const getDuration$1 = (value) => Option.match(recordOption$1(value), {
|
|
81
|
-
onNone: () => 0,
|
|
82
|
-
onSome: (rec) => Option.getOrElse(getNumberField$1(rec, "duration"), () => 0)
|
|
83
|
-
});
|
|
84
|
-
const getFilepath$1 = (value) => Option.match(recordOption$1(value), {
|
|
85
|
-
onNone: () => void 0,
|
|
86
|
-
onSome: (rec) => Option.getOrUndefined(Option.fromNullishOr(rec["filepath"]).pipe(Option.filter((v) => typeof v === "string")))
|
|
87
|
-
});
|
|
88
|
-
const collectSuiteNames$1 = (suite) => Option.match(Option.fromNullishOr(suite), {
|
|
89
|
-
onNone: () => [],
|
|
90
|
-
onSome: (current) => Option.match(recordOption$1(current), {
|
|
91
|
-
onNone: () => [],
|
|
92
|
-
onSome: (rec) => {
|
|
93
|
-
const name = Option.getOrElse(getStringField$1(rec, "name"), () => "");
|
|
94
|
-
const hasName = name.length > 0;
|
|
95
|
-
const parentNames = collectSuiteNames$1(rec["suite"]);
|
|
96
|
-
return Match.value(hasName).pipe(Match.when(true, () => [...parentNames, name]), Match.when(false, () => parentNames), Match.exhaustive);
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
});
|
|
100
|
-
const collectTestNameRaw$1 = (test) => {
|
|
101
|
-
const name = getName$1(test);
|
|
102
|
-
const suite = Option.getOrUndefined(getSuite$1(test));
|
|
103
|
-
return [...collectSuiteNames$1(suite), name].join(" ").trim();
|
|
104
|
-
};
|
|
105
|
-
const toRawTestIdRaw$1 = (test) => {
|
|
106
|
-
return `${Option.match(getFile$1(test), {
|
|
107
|
-
onNone: () => "unknown.js",
|
|
108
|
-
onSome: (file) => Option.getOrElse(Option.fromNullishOr(getFilepath$1(file)), () => "unknown.js")
|
|
109
|
-
})}#${collectTestNameRaw$1(test)}`;
|
|
110
|
-
};
|
|
111
|
-
const stripProjectRoot = (file, projectRoot) => Match.value(file.startsWith(projectRoot)).pipe(Match.when(true, () => file.slice(projectRoot.length)), Match.when(false, () => file), Match.exhaustive);
|
|
112
|
-
const toProjectRelativePath = (file) => file.replace(/^[/\\]+/, "").replaceAll("\\", "/");
|
|
113
|
-
const normalizeTestIdRaw$1 = (id, projectRoot) => {
|
|
114
|
-
const hash = id.indexOf("#");
|
|
115
|
-
return Match.value(hash === -1).pipe(Match.when(true, () => id), Match.when(false, () => {
|
|
116
|
-
return `${toProjectRelativePath(stripProjectRoot(id.slice(0, hash), projectRoot))}#${id.slice(hash + 1)}`;
|
|
117
|
-
}), Match.exhaustive);
|
|
118
|
-
};
|
|
119
|
-
const toTestStatus$1 = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.orElse(() => "failed"))), Match.exhaustive);
|
|
120
|
-
const findSuiteErrorRaw$1 = (suite) => Option.match(Option.fromNullishOr(suite), {
|
|
121
|
-
onNone: () => void 0,
|
|
122
|
-
onSome: (current) => Option.match(recordOption$1(current), {
|
|
123
|
-
onNone: () => void 0,
|
|
124
|
-
onSome: (rec) => {
|
|
125
|
-
const maybeError = Option.flatMap(getResult$1(rec), (result) => Option.flatMap(getErrors$1(result), (errs) => Match.value(errs.length > 0).pipe(Match.when(true, () => Option.flatMap(Option.fromNullishOr(errs[0]), (first) => getMessage$1(first))), Match.when(false, () => Option.none()), Match.exhaustive)));
|
|
126
|
-
return Option.match(maybeError, {
|
|
127
|
-
onNone: () => findSuiteErrorRaw$1(rec["suite"]),
|
|
128
|
-
onSome: (msg) => msg
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
});
|
|
133
|
-
const extractStatus$1 = (test) => {
|
|
134
|
-
const result = Option.getOrUndefined(getResult$1(test));
|
|
135
|
-
const mode = getMode$1(test);
|
|
136
|
-
const state = Option.match(Option.fromNullishOr(result), {
|
|
137
|
-
onNone: () => void 0,
|
|
138
|
-
onSome: (r) => Option.match(recordOption$1(r), {
|
|
139
|
-
onNone: () => void 0,
|
|
140
|
-
onSome: (rec) => getState$1(rec["state"])
|
|
141
|
-
})
|
|
142
|
-
});
|
|
143
|
-
return toTestStatus$1(state, mode);
|
|
144
|
-
};
|
|
145
|
-
const extractDuration$1 = (test) => Option.match(getResult$1(test), {
|
|
146
|
-
onNone: () => 0,
|
|
147
|
-
onSome: (result) => Option.match(recordOption$1(result), {
|
|
148
|
-
onNone: () => 0,
|
|
149
|
-
onSome: (rec) => getDuration$1(rec)
|
|
150
|
-
})
|
|
151
|
-
});
|
|
152
|
-
const extractFileName$1 = (test) => Option.match(getFile$1(test), {
|
|
153
|
-
onNone: () => void 0,
|
|
154
|
-
onSome: (file) => getFilepath$1(file)
|
|
155
|
-
});
|
|
156
|
-
const extractRawId$1 = (test, projectRoot) => normalizeTestIdRaw$1(toRawTestIdRaw$1(test), projectRoot);
|
|
157
|
-
const extractName$1 = (test) => collectTestNameRaw$1(test);
|
|
158
|
-
const extractFailureMessage$1 = (test) => Option.match(getResult$1(test), {
|
|
159
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
160
|
-
onSome: (result) => Option.match(getErrors$1(result), {
|
|
161
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
162
|
-
onSome: (errs) => Match.value(errs.length > 0).pipe(Match.when(true, () => Option.match(Option.fromNullishOr(errs[0]), {
|
|
163
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
164
|
-
onSome: (first) => Option.getOrElse(getMessage$1(first), () => "StrykerJS: Unknown test failure")
|
|
165
|
-
})), Match.when(false, () => "StrykerJS: Unknown test failure"), Match.exhaustive)
|
|
166
|
-
})
|
|
167
|
-
});
|
|
168
|
-
const convertTestRaw$1 = (test, projectRoot) => {
|
|
169
|
-
const status = extractStatus$1(test);
|
|
170
|
-
const base = {
|
|
171
|
-
id: extractRawId$1(test, projectRoot),
|
|
172
|
-
name: extractName$1(test),
|
|
173
|
-
timeSpentMs: extractDuration$1(test),
|
|
174
|
-
fileName: extractFileName$1(test),
|
|
175
|
-
status
|
|
176
|
-
};
|
|
177
|
-
return Match.value(status).pipe(Match.when("failed", () => ({
|
|
178
|
-
...base,
|
|
179
|
-
status,
|
|
180
|
-
failureMessage: extractFailureMessage$1(test)
|
|
181
|
-
})), Match.when("skipped", () => Match.value(findSuiteErrorRaw$1(Option.getOrUndefined(getSuite$1(test)))).pipe(Match.when(Match.defined, (suiteError) => ({
|
|
182
|
-
...base,
|
|
183
|
-
status: "failed",
|
|
184
|
-
failureMessage: suiteError
|
|
185
|
-
})), Match.orElse(() => ({
|
|
186
|
-
...base,
|
|
187
|
-
status
|
|
188
|
-
})))), Match.orElse(() => ({
|
|
189
|
-
...base,
|
|
190
|
-
status
|
|
191
|
-
})));
|
|
192
|
-
};
|
|
193
|
-
/**
|
|
194
|
-
* A run whose tests all passed yet reported an error outside the test files: the shape a dry
|
|
195
|
-
* run reports as `Error` rather than `Complete`.
|
|
196
|
-
*/
|
|
197
|
-
const isSilentExternalError = (tests, command) => Match.value(tests.some((test) => test.status === "failed")).pipe(Match.when(true, () => false), Match.when(false, () => command.hasExternalError), Match.exhaustive);
|
|
198
|
-
const decideVitestDryRun$1 = (command) => Match.value(command.rawTests.map((t) => convertTestRaw$1(t, command.projectRoot))).pipe(Match.when((tests) => isSilentExternalError(tests, command), (tests) => Result.succeed(VitestDryRunOutput.make({
|
|
199
|
-
status: "Error",
|
|
200
|
-
testsJson: JSON.stringify(tests),
|
|
201
|
-
errorMessage: `An error occurred outside of a test run: ${command.externalErrorText}`
|
|
202
|
-
}))), Match.orElse((tests) => Result.succeed(VitestDryRunOutput.make({
|
|
203
|
-
status: "Complete",
|
|
204
|
-
testsJson: JSON.stringify(tests),
|
|
205
|
-
errorMessage: void 0
|
|
206
|
-
}))));
|
|
207
|
-
const hitLimitReason = (hitCount, hitLimit) => Option.flatMap(Option.fromNullishOr(hitCount), (count) => Option.flatMap(Option.fromNullishOr(hitLimit), (limit) => Match.value(count > limit).pipe(Match.when(true, () => Option.some(`Hit limit reached (${count}/${limit})`)), Match.when(false, () => Option.none()), Match.exhaustive)));
|
|
208
|
-
const decideVitestMutantRun = (command) => Match.value(hitLimitReason(command.hitCount, command.hitLimit)).pipe(Match.when(Option.isSome, (hit) => Result.succeed(MutantTimeout.make({
|
|
209
|
-
testsJson: "[]",
|
|
210
|
-
reason: hit.value
|
|
211
|
-
}))), Match.when(Option.isNone, () => {
|
|
212
|
-
const dryOut = decideVitestDryRun$1(VitestDryRunCommand$1.make({
|
|
213
|
-
rawTests: command.rawTests,
|
|
214
|
-
projectRoot: command.projectRoot,
|
|
215
|
-
hasExternalError: command.hasExternalError,
|
|
216
|
-
externalErrorText: command.externalErrorText
|
|
217
|
-
}));
|
|
218
|
-
return Match.value(Result.isFailure(dryOut)).pipe(Match.when(true, () => Result.fail(new VitestMutantRunError({ message: "dry run mapping failed" }))), Match.when(false, () => {
|
|
219
|
-
const dry = Result.getOrElse(dryOut, () => VitestDryRunOutput.make({
|
|
220
|
-
status: "Complete",
|
|
221
|
-
testsJson: "[]",
|
|
222
|
-
errorMessage: void 0
|
|
223
|
-
}));
|
|
224
|
-
return Match.value(dry.status === "Error").pipe(Match.when(true, () => Result.succeed(MutantDryError.make({
|
|
225
|
-
testsJson: "[]",
|
|
226
|
-
errorMessage: dry.errorMessage
|
|
227
|
-
}))), Match.when(false, () => {
|
|
228
|
-
const testsOption = Option.liftThrowable((input) => JSON.parse(input))(dry.testsJson).pipe(Option.filter((v) => Array.isArray(v)));
|
|
229
|
-
const killed = Option.getOrElse(testsOption, () => []).filter((t) => t.status === "failed");
|
|
230
|
-
return Match.value(killed.length > 0).pipe(Match.when(true, () => Match.value(command.reportAllKillers).pipe(Match.when(true, () => {
|
|
231
|
-
const firstKiller = Option.fromNullishOr(killed[0]);
|
|
232
|
-
const failureMessage = Option.match(firstKiller, {
|
|
233
|
-
onNone: () => void 0,
|
|
234
|
-
onSome: (k) => k.failureMessage
|
|
235
|
-
});
|
|
236
|
-
return Result.succeed(MutantKilled.make({
|
|
237
|
-
testsJson: dry.testsJson,
|
|
238
|
-
killerIds: killed.map((t) => t.id),
|
|
239
|
-
failureMessage
|
|
240
|
-
}));
|
|
241
|
-
}), Match.when(false, () => {
|
|
242
|
-
const first = Option.fromNullishOr(killed[0]);
|
|
243
|
-
const failureMessage = Option.match(first, {
|
|
244
|
-
onNone: () => void 0,
|
|
245
|
-
onSome: (k) => k.failureMessage
|
|
246
|
-
});
|
|
247
|
-
return Match.value(Option.isSome(first)).pipe(Match.when(true, () => Result.succeed(MutantKilled.make({
|
|
248
|
-
testsJson: dry.testsJson,
|
|
249
|
-
killerIds: Option.match(first, {
|
|
250
|
-
onNone: () => void 0,
|
|
251
|
-
onSome: (k) => [k.id]
|
|
252
|
-
}),
|
|
253
|
-
failureMessage
|
|
254
|
-
}))), Match.when(false, () => Result.succeed(MutantKilled.make({
|
|
255
|
-
testsJson: dry.testsJson,
|
|
256
|
-
killerIds: Option.getOrUndefined(Option.match(first, {
|
|
257
|
-
onNone: () => Option.none(),
|
|
258
|
-
onSome: (k) => Option.some([k.id])
|
|
259
|
-
})),
|
|
260
|
-
failureMessage
|
|
261
|
-
}))), Match.exhaustive);
|
|
262
|
-
}), Match.exhaustive)), Match.when(false, () => Result.succeed(MutantSurvived.make({ testsJson: dry.testsJson }))), Match.exhaustive);
|
|
263
|
-
}), Match.exhaustive);
|
|
264
|
-
}), Match.exhaustive);
|
|
265
|
-
}), Match.exhaustive);
|
|
266
|
-
const interpretVitestRun = Workflow.make(VitestMutantRunCommand, decideVitestMutantRun);
|
|
267
|
-
//#endregion
|
|
268
|
-
//#region src/Runner.schema.ts
|
|
269
|
-
const VitestRunnerOptionsSchema = S.Struct({
|
|
270
|
-
dir: S.optional(S.String),
|
|
271
|
-
related: S.optional(S.Boolean).pipe(S.withDecodingDefault(Effect.succeed(true))),
|
|
272
|
-
configFile: S.optional(S.String)
|
|
273
|
-
});
|
|
274
|
-
const VitestSectionSchema = S.optional(VitestRunnerOptionsSchema).pipe(S.withDecodingDefault(Effect.succeed({ related: true })));
|
|
275
|
-
const HitCountMetaSchema = S.Struct({ hitCount: S.optional(S.Finite) });
|
|
276
|
-
const MutantCoverageMetaSchema = S.Struct({ mutantCoverage: S.optional(S.Struct({
|
|
277
|
-
static: S.Record(S.String, S.Finite),
|
|
278
|
-
perTest: S.Record(S.String, S.Record(S.String, S.Finite))
|
|
279
|
-
})) });
|
|
280
|
-
const MutantCoverageShapeSchema = S.Struct({
|
|
281
|
-
static: S.Record(S.String, S.Finite),
|
|
282
|
-
perTest: S.Record(S.String, S.Record(S.String, S.Finite))
|
|
283
|
-
});
|
|
284
|
-
var CoverageDecodeFailed = class extends S.TaggedError()("CoverageDecodeFailed", { cause: S.Unknown }) {};
|
|
285
|
-
const ExportEntry = S.Union([S.String, S.Record(S.String, S.Unknown)]);
|
|
286
|
-
const PackageManifest = S.StructWithRest(S.Struct({
|
|
287
|
-
name: S.optional(S.String),
|
|
288
|
-
exports: S.optional(S.Record(S.String, ExportEntry))
|
|
289
|
-
}), [S.Record(S.String, S.Unknown)]);
|
|
290
|
-
const VitestNodeModuleSchema = S.declare((input) => Predicate.isObject(input), { description: "The project-local vitest/node module" });
|
|
291
|
-
const VitestPackageSchema = S.Struct({ version: S.String });
|
|
292
|
-
var VitestDryRunCommand = class extends S.TaggedClass()("VitestDryRunCommand", {
|
|
293
|
-
rawTests: S.Array(S.Unknown),
|
|
294
|
-
projectRoot: S.String,
|
|
295
|
-
hasExternalError: S.Boolean,
|
|
296
|
-
externalErrorText: S.String
|
|
297
|
-
}) {};
|
|
298
|
-
var DryRunComplete = class extends S.TaggedClass()("Complete", { testsJson: S.String }) {};
|
|
299
|
-
var DryRunExternalError = class extends S.TaggedClass()("Error", {
|
|
300
|
-
testsJson: S.String,
|
|
301
|
-
errorMessage: S.String
|
|
302
|
-
}) {};
|
|
303
|
-
//#endregion
|
|
304
|
-
//#region src/Runner.ts
|
|
305
|
-
var VitestHarness = class extends Context.Service()("VitestHarness") {};
|
|
306
|
-
function fromTestId(id) {
|
|
307
|
-
const [file, ...name] = id.split("#");
|
|
308
|
-
return {
|
|
309
|
-
file,
|
|
310
|
-
test: name.join("#")
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
function normalizeTestId(id, projectRoot, pathService) {
|
|
314
|
-
const { file, test } = fromTestId(id);
|
|
315
|
-
return `${normalizeFileName(pathService.relative(projectRoot, file))}#${test}`;
|
|
316
|
-
}
|
|
317
|
-
function normalizeCoverage(rawCoverage, projectRoot, pathService) {
|
|
318
|
-
return {
|
|
319
|
-
perTest: Object.fromEntries(Object.entries(rawCoverage.perTest).map(([rawTestId, coverageData]) => [normalizeTestId(rawTestId, projectRoot, pathService), coverageData])),
|
|
320
|
-
static: rawCoverage.static
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function collectTestsFromSuite(suite) {
|
|
324
|
-
return suite.tasks.flatMap((task) => {
|
|
325
|
-
if (task.type === "suite") return collectTestsFromSuite(task);
|
|
326
|
-
return task;
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
function isErrorCodeError(error) {
|
|
330
|
-
return error instanceof Error && typeof Reflect.get(error, "code") === "string";
|
|
331
|
-
}
|
|
332
|
-
const VITEST_ERROR_CODES = Object.freeze({ FILES_NOT_FOUND: "VITEST_FILES_NOT_FOUND" });
|
|
333
|
-
const recordOption = (value) => S.decodeUnknownOption(S.Record(S.String, S.Unknown))(value);
|
|
334
|
-
const getStringField = (record, key) => Option.fromNullishOr(record[key]).pipe(Option.filter((v) => typeof v === "string"));
|
|
335
|
-
const getNumberField = (record, key) => Option.fromNullishOr(record[key]).pipe(Option.filter((v) => typeof v === "number"));
|
|
336
|
-
const getSuite = (value) => recordOption(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["suite"])));
|
|
337
|
-
const getFile = (value) => recordOption(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["file"])));
|
|
338
|
-
const getResult = (value) => recordOption(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["result"])));
|
|
339
|
-
const getErrors = (value) => recordOption(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["errors"])), Option.filter((v) => Array.isArray(v)));
|
|
340
|
-
const getMessage = (value) => recordOption(value).pipe(Option.flatMap((rec) => Option.fromNullishOr(rec["message"])), Option.filter((v) => typeof v === "string"));
|
|
341
|
-
const getName = (value) => Option.match(recordOption(value), {
|
|
342
|
-
onNone: () => "",
|
|
343
|
-
onSome: (rec) => Option.getOrElse(getStringField(rec, "name"), () => "")
|
|
344
|
-
});
|
|
345
|
-
const getMode = (value) => Option.match(recordOption(value), {
|
|
346
|
-
onNone: () => "run",
|
|
347
|
-
onSome: (rec) => Option.getOrElse(getStringField(rec, "mode"), () => "run")
|
|
348
|
-
});
|
|
349
|
-
const getState = (value) => Match.value(value).pipe(Match.when("pass", () => "pass"), Match.when("fail", () => "fail"), Match.when("skip", () => "skip"), Match.when("todo", () => "todo"), Match.when("run", () => "run"), Match.when("queued", () => "queued"), Match.when("only", () => "only"), Match.when(void 0, () => void 0), Match.orElse(() => void 0));
|
|
350
|
-
const getDuration = (value) => Option.match(recordOption(value), {
|
|
351
|
-
onNone: () => 0,
|
|
352
|
-
onSome: (rec) => Option.getOrElse(getNumberField(rec, "duration"), () => 0)
|
|
353
|
-
});
|
|
354
|
-
const getFilepath = (value) => Option.match(recordOption(value), {
|
|
355
|
-
onNone: () => void 0,
|
|
356
|
-
onSome: (rec) => Option.getOrUndefined(Option.fromNullishOr(rec["filepath"]).pipe(Option.filter((v) => typeof v === "string")))
|
|
357
|
-
});
|
|
358
|
-
const collectSuiteNames = (suite) => Option.match(Option.fromNullishOr(suite), {
|
|
359
|
-
onNone: () => [],
|
|
360
|
-
onSome: (current) => Option.match(recordOption(current), {
|
|
361
|
-
onNone: () => [],
|
|
362
|
-
onSome: (rec) => {
|
|
363
|
-
const name = Option.getOrElse(getStringField(rec, "name"), () => "");
|
|
364
|
-
const hasName = name.length > 0;
|
|
365
|
-
const parentNames = collectSuiteNames(rec["suite"]);
|
|
366
|
-
return Match.value(hasName).pipe(Match.when(true, () => [...parentNames, name]), Match.when(false, () => parentNames), Match.exhaustive);
|
|
367
|
-
}
|
|
368
|
-
})
|
|
369
|
-
});
|
|
370
|
-
const collectTestNameRaw = (test) => {
|
|
371
|
-
const name = getName(test);
|
|
372
|
-
const suite = Option.getOrUndefined(getSuite(test));
|
|
373
|
-
return [...collectSuiteNames(suite), name].join(" ").trim();
|
|
374
|
-
};
|
|
375
|
-
const toRawTestIdRaw = (test) => {
|
|
376
|
-
return `${Option.match(getFile(test), {
|
|
377
|
-
onNone: () => "unknown.js",
|
|
378
|
-
onSome: (file) => Option.getOrElse(Option.fromNullishOr(getFilepath(file)), () => "unknown.js")
|
|
379
|
-
})}#${collectTestNameRaw(test)}`;
|
|
380
|
-
};
|
|
381
|
-
const normalizeTestIdRaw = (id, projectRoot) => {
|
|
382
|
-
const hash = id.indexOf("#");
|
|
383
|
-
if (hash === -1) return id;
|
|
384
|
-
const file = id.slice(0, hash);
|
|
385
|
-
const rest = id.slice(hash + 1);
|
|
386
|
-
return `${(() => {
|
|
387
|
-
if (file.startsWith(projectRoot)) return file.slice(projectRoot.length);
|
|
388
|
-
return file;
|
|
389
|
-
})().replace(/^[/\\]+/, "").replaceAll("\\", "/")}#${rest}`;
|
|
390
|
-
};
|
|
391
|
-
const toTestStatus = (taskState, mode) => Match.value(mode === "skip").pipe(Match.when(true, () => "skipped"), Match.when(false, () => Match.value(taskState).pipe(Match.when("pass", () => "success"), Match.when("skip", () => "skipped"), Match.when("todo", () => "skipped"), Match.orElse(() => "failed"))), Match.exhaustive);
|
|
392
|
-
const findSuiteErrorRaw = (suite) => Option.match(Option.fromNullishOr(suite), {
|
|
393
|
-
onNone: () => void 0,
|
|
394
|
-
onSome: (current) => Option.match(recordOption(current), {
|
|
395
|
-
onNone: () => void 0,
|
|
396
|
-
onSome: (rec) => {
|
|
397
|
-
const maybeError = Option.flatMap(getResult(rec), (result) => Option.flatMap(getErrors(result), (errs) => Match.value(errs.length > 0).pipe(Match.when(true, () => Option.flatMap(Option.fromNullishOr(errs[0]), (first) => getMessage(first))), Match.when(false, () => Option.none()), Match.exhaustive)));
|
|
398
|
-
return Option.match(maybeError, {
|
|
399
|
-
onNone: () => findSuiteErrorRaw(rec["suite"]),
|
|
400
|
-
onSome: (msg) => msg
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
})
|
|
404
|
-
});
|
|
405
|
-
const extractStatus = (test) => {
|
|
406
|
-
const result = Option.getOrUndefined(getResult(test));
|
|
407
|
-
const mode = getMode(test);
|
|
408
|
-
const state = Option.match(Option.fromNullishOr(result), {
|
|
409
|
-
onNone: () => void 0,
|
|
410
|
-
onSome: (r) => Option.match(recordOption(r), {
|
|
411
|
-
onNone: () => void 0,
|
|
412
|
-
onSome: (rec) => getState(rec["state"])
|
|
413
|
-
})
|
|
414
|
-
});
|
|
415
|
-
return toTestStatus(state, mode);
|
|
416
|
-
};
|
|
417
|
-
const extractDuration = (test) => Option.match(getResult(test), {
|
|
418
|
-
onNone: () => 0,
|
|
419
|
-
onSome: (result) => Option.match(recordOption(result), {
|
|
420
|
-
onNone: () => 0,
|
|
421
|
-
onSome: (rec) => getDuration(rec)
|
|
422
|
-
})
|
|
423
|
-
});
|
|
424
|
-
const extractFileName = (test) => Option.match(getFile(test), {
|
|
425
|
-
onNone: () => void 0,
|
|
426
|
-
onSome: (file) => getFilepath(file)
|
|
427
|
-
});
|
|
428
|
-
const extractRawId = (test, projectRoot) => normalizeTestIdRaw(toRawTestIdRaw(test), projectRoot);
|
|
429
|
-
const extractName = (test) => collectTestNameRaw(test);
|
|
430
|
-
const extractFailureMessage = (test) => Option.match(getResult(test), {
|
|
431
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
432
|
-
onSome: (result) => Option.match(getErrors(result), {
|
|
433
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
434
|
-
onSome: (errs) => Match.value(errs.length > 0).pipe(Match.when(true, () => Option.match(Option.fromNullishOr(errs[0]), {
|
|
435
|
-
onNone: () => "StrykerJS: Unknown test failure",
|
|
436
|
-
onSome: (first) => Option.getOrElse(getMessage(first), () => "StrykerJS: Unknown test failure")
|
|
437
|
-
})), Match.when(false, () => "StrykerJS: Unknown test failure"), Match.exhaustive)
|
|
438
|
-
})
|
|
439
|
-
});
|
|
440
|
-
const convertTestRaw = (test, projectRoot) => {
|
|
441
|
-
const status = extractStatus(test);
|
|
442
|
-
const base = {
|
|
443
|
-
id: extractRawId(test, projectRoot),
|
|
444
|
-
name: extractName(test),
|
|
445
|
-
timeSpentMs: extractDuration(test),
|
|
446
|
-
fileName: extractFileName(test),
|
|
447
|
-
status
|
|
448
|
-
};
|
|
449
|
-
return Match.value(status).pipe(Match.when("failed", () => ({
|
|
450
|
-
...base,
|
|
451
|
-
status,
|
|
452
|
-
failureMessage: extractFailureMessage(test)
|
|
453
|
-
})), Match.when("skipped", () => Match.value(findSuiteErrorRaw(Option.getOrUndefined(getSuite(test)))).pipe(Match.when(Match.defined, (suiteError) => ({
|
|
454
|
-
...base,
|
|
455
|
-
status: "failed",
|
|
456
|
-
failureMessage: suiteError
|
|
457
|
-
})), Match.orElse(() => ({
|
|
458
|
-
...base,
|
|
459
|
-
status
|
|
460
|
-
})))), Match.orElse(() => ({
|
|
461
|
-
...base,
|
|
462
|
-
status
|
|
463
|
-
})));
|
|
464
|
-
};
|
|
465
|
-
const decideVitestDryRun = (command) => {
|
|
466
|
-
const tests = command.rawTests.map((t) => convertTestRaw(t, command.projectRoot));
|
|
467
|
-
const testsJson = JSON.stringify(tests);
|
|
468
|
-
const hasFailure = tests.some((t) => t.status === "failed");
|
|
469
|
-
return Match.value(hasFailure).pipe(Match.when(true, () => DryRunComplete.make({ testsJson })), Match.orElse(() => Match.value(command.hasExternalError).pipe(Match.when(true, () => DryRunExternalError.make({
|
|
470
|
-
testsJson,
|
|
471
|
-
errorMessage: `An error occurred outside of a test run: ${command.externalErrorText}`
|
|
472
|
-
})), Match.orElse(() => DryRunComplete.make({ testsJson })))));
|
|
473
|
-
};
|
|
474
|
-
const TYPESCRIPT_SOURCE_EXTENSIONS = [
|
|
475
|
-
".ts",
|
|
476
|
-
".tsx",
|
|
477
|
-
".mts"
|
|
478
|
-
];
|
|
479
|
-
const isTypescriptSourcePath = (filePath) => TYPESCRIPT_SOURCE_EXTENSIONS.some((extension) => filePath.endsWith(extension));
|
|
480
|
-
const typescriptSourcePath = (filePath) => Option.getOrUndefined(Option.filter(Option.some(filePath), isTypescriptSourcePath));
|
|
481
|
-
const sourceTargetOf = (entry) => Match.value(entry).pipe(Match.when(Match.string, (filePath) => typescriptSourcePath(filePath)), Match.orElse(() => void 0));
|
|
482
|
-
const subpathSpecifier = (packageName, exportKey) => Match.value(exportKey.startsWith("./")).pipe(Match.when(true, () => `${packageName}/${exportKey.slice(2)}`), Match.orElse(() => void 0));
|
|
483
|
-
const specifierForExport = (packageName, exportKey) => Match.value(exportKey).pipe(Match.when(".", () => packageName), Match.when("./package.json", () => void 0), Match.orElse((key) => subpathSpecifier(packageName, key)));
|
|
484
|
-
const namedExports = (manifest) => Option.flatMap(Option.filter(Option.fromNullishOr(manifest.name), (name) => name.length > 0), (name) => Option.map(Option.fromNullishOr(manifest.exports), (exportMap) => ({
|
|
485
|
-
name,
|
|
486
|
-
exports: exportMap
|
|
487
|
-
})));
|
|
488
|
-
const exportAlias = (packageName, projectRoot, pathService, [exportKey, entry]) => Option.flatMap(Option.fromNullishOr(specifierForExport(packageName, exportKey)), (spec) => Option.map(Option.fromNullishOr(sourceTargetOf(entry)), (target) => ({
|
|
489
|
-
find: new RegExp(`^${RegExp.escape(spec)}$`),
|
|
490
|
-
replacement: pathService.resolve(projectRoot, target)
|
|
491
|
-
})));
|
|
492
|
-
const sandboxSelfAliases = (manifest, projectRoot, pathService) => Option.match(namedExports(manifest), {
|
|
493
|
-
onNone: () => [],
|
|
494
|
-
onSome: ({ name, exports: exportMap }) => Object.entries(exportMap).flatMap((entry) => Option.toArray(exportAlias(name, projectRoot, pathService, entry)))
|
|
495
|
-
});
|
|
496
|
-
const parseJson = (text) => {
|
|
497
|
-
try {
|
|
498
|
-
return JSON.parse(text);
|
|
499
|
-
} catch {
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
const readSandboxSelfAliases = (projectRoot) => Effect$1.gen(function* () {
|
|
504
|
-
const fs = yield* FileSystem.FileSystem;
|
|
505
|
-
const pathService = yield* Path.Path;
|
|
506
|
-
const raw = yield* fs.readFileString(pathService.join(projectRoot, "package.json")).pipe(Effect$1.orElseSucceed(() => null));
|
|
507
|
-
return Option.match(Option.flatMap(Option.fromNullishOr(raw), (content) => S.decodeUnknownOption(PackageManifest)(parseJson(content))), {
|
|
508
|
-
onNone: () => [],
|
|
509
|
-
onSome: (manifest) => sandboxSelfAliases(manifest, projectRoot, pathService)
|
|
510
|
-
});
|
|
511
|
-
});
|
|
512
|
-
const sandboxSelfPlugin = (aliases) => ({
|
|
513
|
-
name: "stryker-sandbox-self-exports",
|
|
514
|
-
enforce: "pre",
|
|
515
|
-
resolveId(source) {
|
|
516
|
-
return Option.getOrUndefined(Option.map(Option.fromNullishOr(aliases.find((alias) => alias.find.test(source))), (alias) => alias.replacement));
|
|
517
|
-
}
|
|
518
|
-
});
|
|
519
|
-
const isRunnerTestSuite = (value) => Predicate.isObject(value) && Array.isArray(value["tasks"]);
|
|
520
|
-
const STRYKER_SETUP_URL = new URL("./stryker-setup.mjs", import.meta.url);
|
|
521
|
-
const resolveVitest = (_dir) => Effect$1.gen(function* () {
|
|
522
|
-
const fallback = Effect$1.gen(function* () {
|
|
523
|
-
const pathService = yield* Path.Path;
|
|
524
|
-
const fs = yield* FileSystem.FileSystem;
|
|
525
|
-
const urlString = import.meta.resolve("vitest/package.json");
|
|
526
|
-
const packageJsonPath = yield* pathService.fromFileUrl(new URL(urlString));
|
|
527
|
-
const content = yield* fs.readFileString(packageJsonPath);
|
|
528
|
-
const parsed = JSON.parse(content);
|
|
529
|
-
const decoded = yield* S.decodeUnknownEffect(VitestPackageSchema)(parsed);
|
|
530
|
-
return {
|
|
531
|
-
createVitest,
|
|
532
|
-
version: decoded.version
|
|
533
|
-
};
|
|
534
|
-
}).pipe(Effect$1.orDie);
|
|
535
|
-
return yield* Effect$1.gen(function* () {
|
|
536
|
-
const module = yield* Module;
|
|
537
|
-
const pathService = yield* Path.Path;
|
|
538
|
-
const fs = yield* FileSystem.FileSystem;
|
|
539
|
-
const requireFromProject = module.createRequire(pathService.join(_dir, "package.json"));
|
|
540
|
-
const imported = requireFromProject("vitest/node");
|
|
541
|
-
const decodedNode = yield* S.decodeUnknownEffect(VitestNodeModuleSchema)(imported);
|
|
542
|
-
const packageJsonPath = requireFromProject.resolve("vitest/package.json");
|
|
543
|
-
const content = yield* fs.readFileString(packageJsonPath);
|
|
544
|
-
const parsed = JSON.parse(content);
|
|
545
|
-
const decodedPackage = yield* S.decodeUnknownEffect(VitestPackageSchema)(parsed);
|
|
546
|
-
return {
|
|
547
|
-
createVitest: decodedNode.createVitest,
|
|
548
|
-
version: decodedPackage.version
|
|
549
|
-
};
|
|
550
|
-
}).pipe(Effect$1.catchCause(() => fallback), Effect$1.catchDefect(() => fallback));
|
|
551
|
-
}).pipe(Effect$1.orDie);
|
|
552
|
-
const versionPart = (parts, index) => Option.getOrElse(Option.map(Option.fromNullishOr(parts[index]), (part) => Number(part)), () => 0);
|
|
553
|
-
const minimumMinorForMajor = (major) => Match.value(major).pipe(Match.when((value) => value > 4, () => Option.some(0)), Match.when(4, () => Option.some(1)), Match.orElse(() => Option.none()));
|
|
554
|
-
const shouldUseSuiteMetaSecondArg = (version) => {
|
|
555
|
-
const parts = version.split(".");
|
|
556
|
-
const major = versionPart(parts, 0);
|
|
557
|
-
const minor = versionPart(parts, 1);
|
|
558
|
-
return Match.value(Number.isNaN(major) || Number.isNaN(minor)).pipe(Match.when(true, () => false), Match.orElse(() => Option.exists(minimumMinorForMajor(major), (minimumMinor) => minor >= minimumMinor)));
|
|
559
|
-
};
|
|
560
|
-
const relatedFilesOf = (relatedValue, relatedFiles) => Match.value(relatedValue !== false).pipe(Match.when(true, () => Option.getOrUndefined(Option.map(Option.fromNullishOr(relatedFiles), (files) => files.map(normalizeFileName)))), Match.orElse(() => void 0));
|
|
561
|
-
const testIdPlan = (testIds, projectRoot, pathService) => Option.map(Option.filter(Option.fromNullishOr(testIds), (ids) => ids.length > 0), (ids) => ({
|
|
562
|
-
testNamePattern: new RegExp(ids.map((id) => RegExp.escape(fromTestId(id).test)).join("|")),
|
|
563
|
-
testFiles: ids.map((id) => pathService.resolve(projectRoot, fromTestId(id).file))
|
|
564
|
-
}));
|
|
565
|
-
const runFilterPlan = (filter, projectRoot, pathService) => {
|
|
566
|
-
const plan = testIdPlan(filter.testIds, projectRoot, pathService);
|
|
567
|
-
return {
|
|
568
|
-
testNamePattern: Option.getOrUndefined(Option.map(plan, (value) => value.testNamePattern)),
|
|
569
|
-
testFiles: Option.match(plan, {
|
|
570
|
-
onNone: () => Option.getOrUndefined(Option.map(Option.fromNullishOr(filter.testFiles), (files) => [...files])),
|
|
571
|
-
onSome: (value) => value.testFiles
|
|
572
|
-
})
|
|
573
|
-
};
|
|
574
|
-
};
|
|
575
|
-
const isMissingTestFilesCause = (cause) => Match.value(isErrorCodeError(cause)).pipe(Match.when(true, () => typeof cause === "string" && cause.includes(VITEST_ERROR_CODES.FILES_NOT_FOUND)), Match.orElse(() => false));
|
|
576
|
-
const experimentalStateGetFiles = (vitest) => vitest.state.getFiles();
|
|
577
|
-
const propertyOf = (value, key) => Option.flatMap(Option.filter(Option.fromNullishOr(value), Predicate.isObject), (record) => Option.fromNullishOr(record[key]));
|
|
578
|
-
const vitestStateOf = (vitest) => propertyOf(vitest, "state");
|
|
579
|
-
const errorsSetOf = (vitest) => Option.flatMap(vitestStateOf(vitest), (state) => propertyOf(state, "errorsSet"));
|
|
580
|
-
const invokeMethod = (holder, name) => Option.match(Option.filter(propertyOf(holder, name), Predicate.isFunction), {
|
|
581
|
-
onNone: () => void 0,
|
|
582
|
-
onSome: (method) => {
|
|
583
|
-
Reflect.apply(method, holder, []);
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
const clearFilesMap = (filesMap) => Match.value(filesMap).pipe(Match.when(Match.instanceOf(Map), (map) => {
|
|
587
|
-
map.clear();
|
|
588
|
-
}), Match.orElse((value) => invokeMethod(value, "clear")));
|
|
589
|
-
const experimentalStateClearFiles = (vitest) => Option.match(Option.flatMap(vitestStateOf(vitest), (state) => propertyOf(state, "filesMap")), {
|
|
590
|
-
onNone: () => void 0,
|
|
591
|
-
onSome: (filesMap) => clearFilesMap(filesMap)
|
|
592
|
-
});
|
|
593
|
-
const entryCountOf = (collection) => Match.value(collection).pipe(Match.when(Match.instanceOf(Set), (set) => Option.some(set.size)), Match.orElse((value) => Option.filter(propertyOf(value, "size"), Predicate.isNumber)));
|
|
594
|
-
const experimentalStateHasExternalErrors = (vitest) => Option.exists(Option.flatMap(errorsSetOf(vitest), entryCountOf), (count) => count > 0);
|
|
595
|
-
const experimentalStateGetExternalErrorText = (vitest) => Option.match(errorsSetOf(vitest), {
|
|
596
|
-
onNone: () => "",
|
|
597
|
-
onSome: (errorsSet) => Match.value(errorsSet).pipe(Match.when(Predicate.isIterable, (errors) => [...errors].map(errorToString).join("\n")), Match.orElse(() => ""))
|
|
598
|
-
});
|
|
599
|
-
const applyHarnessValue = (ctx, key, value) => Match.value(key).pipe(Match.when("hitLimit", () => {
|
|
600
|
-
ctx.provide("hitLimit", Option.getOrUndefined(Option.filter(Option.fromNullishOr(value), Predicate.isNumber)));
|
|
601
|
-
}), Match.when("mutantActivation", () => {
|
|
602
|
-
Match.value(value).pipe(Match.when(Match.is("runtime", "static"), (activation) => {
|
|
603
|
-
ctx.provide("mutantActivation", activation);
|
|
604
|
-
}), Match.orElse(() => void 0));
|
|
605
|
-
}), Match.orElse(() => {
|
|
606
|
-
Match.value(value).pipe(Match.when(Predicate.isString, (activeMutant) => {
|
|
607
|
-
ctx.provide("activeMutant", activeMutant);
|
|
608
|
-
}), Match.orElse(() => void 0));
|
|
609
|
-
}));
|
|
610
|
-
const applyRunFilterToConfig = (vitest, options) => {
|
|
611
|
-
Reflect.set(vitest.config, "related", options.related);
|
|
612
|
-
for (const project of vitest.projects) Reflect.set(project.config, "testNamePattern", options.testNamePattern);
|
|
613
|
-
};
|
|
614
|
-
const disableScreenshotFailures = (value) => Option.match(Option.filter(Option.fromNullishOr(value), Predicate.isObject), {
|
|
615
|
-
onNone: () => void 0,
|
|
616
|
-
onSome: (browser) => {
|
|
617
|
-
Reflect.set(browser, "screenshotFailures", false);
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
const setupFilePathsOf = (value) => Match.value(value).pipe(Match.when(Array.isArray, (setupFiles) => setupFiles.filter(Predicate.isString)), Match.orElse(() => []));
|
|
621
|
-
const applySetupFilesToProjects = (vitest, localSetupFile) => {
|
|
622
|
-
disableScreenshotFailures(Reflect.get(vitest.config, "browser"));
|
|
623
|
-
for (const project of vitest.projects) {
|
|
624
|
-
const setupFiles = setupFilePathsOf(Reflect.get(project.config, "setupFiles"));
|
|
625
|
-
Reflect.set(project.config, "setupFiles", [localSetupFile, ...setupFiles]);
|
|
626
|
-
disableScreenshotFailures(Reflect.get(project.config, "browser"));
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
const makeVitestRunnerLayer = (input) => Layer.effect(TestRunner, Effect$1.gen(function* () {
|
|
630
|
-
const stateRef = yield* Ref.make({
|
|
631
|
-
ctx: void 0,
|
|
632
|
-
localSetupFile: void 0
|
|
633
|
-
});
|
|
634
|
-
const fsService = yield* FileSystem.FileSystem;
|
|
635
|
-
const pathService = yield* Path.Path;
|
|
636
|
-
const moduleService = yield* Module;
|
|
637
|
-
const getState = Ref.get(stateRef);
|
|
638
|
-
const requireCtx = Effect$1.gen(function* () {
|
|
639
|
-
const state = yield* getState;
|
|
640
|
-
if (state.ctx === void 0) return yield* new TestRunnerFailed({
|
|
641
|
-
runnerName: "vitest",
|
|
642
|
-
phase: "dryRun",
|
|
643
|
-
cause: errorToString(/* @__PURE__ */ new Error("Vitest runner is not initialized; call init() before running tests"))
|
|
644
|
-
});
|
|
645
|
-
return state.ctx;
|
|
646
|
-
});
|
|
647
|
-
const decodedOptionsEffect = (raw) => S.decodeUnknownEffect(VitestSectionSchema)(raw).pipe(Effect$1.map((decoded) => (() => {
|
|
648
|
-
if (decoded === void 0) return { related: true };
|
|
649
|
-
return decoded;
|
|
650
|
-
})()), Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
651
|
-
runnerName: "vitest",
|
|
652
|
-
phase: "init",
|
|
653
|
-
cause: errorToString(cause)
|
|
654
|
-
})));
|
|
655
|
-
const optionsEffect = decodedOptionsEffect(Reflect.get(input.options, "vitest")).pipe(Effect$1.map((vitestOptions) => ({
|
|
656
|
-
...input.options,
|
|
657
|
-
vitest: vitestOptions
|
|
658
|
-
})));
|
|
659
|
-
const capabilities = Effect$1.succeed({ reloadEnvironment: true });
|
|
660
|
-
const init = Effect$1.gen(function* () {
|
|
661
|
-
const options = yield* optionsEffect;
|
|
662
|
-
yield* Effect$1.sync(() => {
|
|
663
|
-
process.env.NODE_ENV = "test";
|
|
664
|
-
process.env.VITEST = "1";
|
|
665
|
-
});
|
|
666
|
-
const projectRoot = input.sandboxDirectory;
|
|
667
|
-
const localSetupFile = pathService.resolve(projectRoot, `stryker-setup-${process.pid}.js`);
|
|
668
|
-
yield* Ref.update(stateRef, (s) => ({
|
|
669
|
-
...s,
|
|
670
|
-
localSetupFile
|
|
671
|
-
}));
|
|
672
|
-
const defaultSetupPath = yield* pathService.fromFileUrl(STRYKER_SETUP_URL).pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
673
|
-
runnerName: "vitest",
|
|
674
|
-
phase: "init",
|
|
675
|
-
cause: errorToString(cause)
|
|
676
|
-
})));
|
|
677
|
-
const setupFilePath = Option.getOrElse(Option.fromNullishOr(input.setupFilePath), () => defaultSetupPath);
|
|
678
|
-
yield* fsService.copyFile(setupFilePath, localSetupFile).pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
679
|
-
runnerName: "vitest",
|
|
680
|
-
phase: "init",
|
|
681
|
-
cause: errorToString(cause)
|
|
682
|
-
})));
|
|
683
|
-
const { createVitest, version } = yield* Option.getOrElse(Option.fromNullishOr(input.resolveVitestFor), () => resolveVitest)(projectRoot).pipe(Effect$1.provideService(Module, moduleService), Effect$1.provideService(FileSystem.FileSystem, fsService), Effect$1.provideService(Path.Path, pathService), Effect$1.catchDefect((cause) => Effect$1.fail(new TestRunnerFailed({
|
|
684
|
-
runnerName: "vitest",
|
|
685
|
-
phase: "init",
|
|
686
|
-
cause: errorToString(cause)
|
|
687
|
-
}))));
|
|
688
|
-
const namespace = Option.getOrElse(Option.fromNullishOr(input.globalNamespace), () => INSTRUMENTER_CONSTANTS.NAMESPACE);
|
|
689
|
-
const scanDir = (() => {
|
|
690
|
-
if (typeof options.vitest.dir === "string") return pathService.resolve(projectRoot, options.vitest.dir);
|
|
691
|
-
})();
|
|
692
|
-
const aliases = yield* readSandboxSelfAliases(projectRoot).pipe(Effect$1.provideService(FileSystem.FileSystem, fsService), Effect$1.provideService(Path.Path, pathService));
|
|
693
|
-
const plugin = sandboxSelfPlugin(aliases);
|
|
694
|
-
const ctx = yield* Effect$1.tryPromise({
|
|
695
|
-
try: () => createVitest("test", {
|
|
696
|
-
config: options.vitest.configFile,
|
|
697
|
-
coverage: { enabled: false },
|
|
698
|
-
maxWorkers: 1,
|
|
699
|
-
maxConcurrency: 1,
|
|
700
|
-
watch: false,
|
|
701
|
-
root: projectRoot,
|
|
702
|
-
...(() => {
|
|
703
|
-
if (scanDir === void 0) return {};
|
|
704
|
-
return { dir: scanDir };
|
|
705
|
-
})(),
|
|
706
|
-
bail: (() => {
|
|
707
|
-
if (options.disableBail) return 0;
|
|
708
|
-
return 1;
|
|
709
|
-
})(),
|
|
710
|
-
onConsoleLog: () => false,
|
|
711
|
-
silent: true,
|
|
712
|
-
reporters: [{ onInit(_vitest) {} }]
|
|
713
|
-
}, {
|
|
714
|
-
resolve: {
|
|
715
|
-
alias: [...aliases],
|
|
716
|
-
conditions: ["import"]
|
|
717
|
-
},
|
|
718
|
-
plugins: [plugin]
|
|
719
|
-
}),
|
|
720
|
-
catch: (cause) => new TestRunnerFailed({
|
|
721
|
-
runnerName: "vitest",
|
|
722
|
-
phase: "init",
|
|
723
|
-
cause: errorToString(cause)
|
|
724
|
-
})
|
|
725
|
-
});
|
|
726
|
-
ctx.provide("globalNamespace", namespace);
|
|
727
|
-
ctx.provide("isGreaterThanVitest4Point1", shouldUseSuiteMetaSecondArg(version));
|
|
728
|
-
applySetupFilesToProjects(ctx, localSetupFile);
|
|
729
|
-
yield* Ref.update(stateRef, (s) => ({
|
|
730
|
-
...s,
|
|
731
|
-
ctx
|
|
732
|
-
}));
|
|
733
|
-
}).pipe(Effect$1.mapError((cause) => (() => {
|
|
734
|
-
if (cause instanceof TestRunnerFailed) return cause;
|
|
735
|
-
return new TestRunnerFailed({
|
|
736
|
-
runnerName: "vitest",
|
|
737
|
-
phase: "init",
|
|
738
|
-
cause: errorToString(cause)
|
|
739
|
-
});
|
|
740
|
-
})()));
|
|
741
|
-
const resetContext = Effect$1.gen(function* () {
|
|
742
|
-
const ctx = yield* requireCtx;
|
|
743
|
-
experimentalStateClearFiles(ctx);
|
|
744
|
-
});
|
|
745
|
-
const getFileMeta = (file) => Option.getOrUndefined(propertyOf(file, "meta"));
|
|
746
|
-
const readHitCount = Effect$1.gen(function* () {
|
|
747
|
-
const ctx = yield* requireCtx.pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })));
|
|
748
|
-
return (yield* Effect$1.forEach(experimentalStateGetFiles(ctx), (file) => Effect$1.map(S.decodeUnknownEffect(HitCountMetaSchema)(getFileMeta(file)).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ hitCount: void 0 }))), (decoded) => Option.getOrElse(Option.fromNullishOr(decoded.hitCount), () => 0)))).reduce((total, count) => total + count, 0);
|
|
749
|
-
});
|
|
750
|
-
const stringProperty = (value, key) => Option.getOrElse(Option.filter(propertyOf(value, key), Predicate.isString), () => "");
|
|
751
|
-
const dedupeFilesByName = (files) => Object.fromEntries(files.map((file) => [`${stringProperty(file, "projectName")}-${stringProperty(file, "name")}`, file]));
|
|
752
|
-
const validateCoverage = (mutantCoverage) => {
|
|
753
|
-
const normalized = normalizeCoverage(mutantCoverage, input.sandboxDirectory, pathService);
|
|
754
|
-
return S.decodeEffect(MutantCoverageShapeSchema)(normalized).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.map(() => normalized));
|
|
755
|
-
};
|
|
756
|
-
const coverageOfFile = (file) => Effect$1.gen(function* () {
|
|
757
|
-
const decoded = yield* S.decodeUnknownEffect(MutantCoverageMetaSchema)(getFileMeta(file)).pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })), Effect$1.orElseSucceed(() => ({ mutantCoverage: void 0 })));
|
|
758
|
-
return yield* Option.match(Option.fromNullishOr(decoded.mutantCoverage), {
|
|
759
|
-
onNone: () => Effect$1.succeed(void 0),
|
|
760
|
-
onSome: (mutantCoverage) => validateCoverage(mutantCoverage)
|
|
761
|
-
});
|
|
762
|
-
});
|
|
763
|
-
const mergeTestCoverage = (perTest, testId, coverage) => Option.match(Option.fromNullishOr(perTest[testId]), {
|
|
764
|
-
onNone: () => {
|
|
765
|
-
perTest[testId] = coverage;
|
|
766
|
-
},
|
|
767
|
-
onSome: (existing) => {
|
|
768
|
-
mergeCoverage(existing, coverage);
|
|
769
|
-
}
|
|
770
|
-
});
|
|
771
|
-
const mergeProjectCoverage = (acc, projectCoverage) => {
|
|
772
|
-
for (const [testId, testCoverage] of Object.entries(projectCoverage.perTest)) mergeTestCoverage(acc.perTest, testId, testCoverage);
|
|
773
|
-
mergeCoverage(acc.static, projectCoverage.static);
|
|
774
|
-
return acc;
|
|
775
|
-
};
|
|
776
|
-
const readMutantCoverage = Effect$1.gen(function* () {
|
|
777
|
-
const ctx = yield* requireCtx.pipe(Effect$1.mapError((cause) => new CoverageDecodeFailed({ cause })));
|
|
778
|
-
const files = Object.values(dedupeFilesByName(experimentalStateGetFiles(ctx)));
|
|
779
|
-
const coverages = (yield* Effect$1.forEach(files, coverageOfFile)).filter(Predicate.isNotNullish);
|
|
780
|
-
return Option.getOrUndefined(Option.map(Option.fromNullishOr(coverages[0]), (first) => coverages.slice(1).reduce(mergeProjectCoverage, first)));
|
|
781
|
-
});
|
|
782
|
-
const collectRaw = (filter) => Effect$1.gen(function* () {
|
|
783
|
-
const ctx = yield* requireCtx;
|
|
784
|
-
const options = yield* optionsEffect;
|
|
785
|
-
yield* resetContext.pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
786
|
-
runnerName: "vitest",
|
|
787
|
-
phase: "dryRun",
|
|
788
|
-
cause: errorToString(cause)
|
|
789
|
-
})));
|
|
790
|
-
const vitestInRun = Reflect.get(options, "vitest");
|
|
791
|
-
const related = relatedFilesOf(Reflect.get(vitestInRun, "related"), filter.relatedFiles);
|
|
792
|
-
const plan = runFilterPlan(filter, input.sandboxDirectory, pathService);
|
|
793
|
-
applyRunFilterToConfig(ctx, {
|
|
794
|
-
related,
|
|
795
|
-
testNamePattern: plan.testNamePattern
|
|
796
|
-
});
|
|
797
|
-
yield* Effect$1.tryPromise({
|
|
798
|
-
try: () => ctx.start(plan.testFiles),
|
|
799
|
-
catch: (cause) => new TestRunnerFailed({
|
|
800
|
-
runnerName: "vitest",
|
|
801
|
-
phase: "dryRun",
|
|
802
|
-
cause: errorToString(cause)
|
|
803
|
-
})
|
|
804
|
-
}).pipe(Effect$1.catchIf((error) => isMissingTestFilesCause(error.cause), () => Effect$1.void));
|
|
805
|
-
const rawTests = experimentalStateGetFiles(ctx).flatMap((file) => (() => {
|
|
806
|
-
if (isRunnerTestSuite(file)) return collectTestsFromSuite(file);
|
|
807
|
-
return [];
|
|
808
|
-
})()).filter((test) => test.result !== void 0);
|
|
809
|
-
const hasExternalError = experimentalStateHasExternalErrors(ctx);
|
|
810
|
-
return {
|
|
811
|
-
rawTests,
|
|
812
|
-
hasExternalError,
|
|
813
|
-
externalErrorText: Match.value(hasExternalError).pipe(Match.when(true, () => experimentalStateGetExternalErrorText(ctx)), Match.orElse(() => ""))
|
|
814
|
-
};
|
|
815
|
-
});
|
|
816
|
-
const harnessImpl = {
|
|
817
|
-
setMode: (mode) => Effect$1.gen(function* () {
|
|
818
|
-
(yield* requireCtx).provide("mode", mode);
|
|
819
|
-
}),
|
|
820
|
-
provide: (key, value) => Effect$1.gen(function* () {
|
|
821
|
-
const ctx = yield* requireCtx;
|
|
822
|
-
applyHarnessValue(ctx, key, value);
|
|
823
|
-
})
|
|
824
|
-
};
|
|
825
|
-
const mutantRunCell = Cell.layer({
|
|
826
|
-
read: (command) => Effect$1.gen(function* () {
|
|
827
|
-
const harness = yield* VitestHarness;
|
|
828
|
-
yield* harness.setMode("mutant");
|
|
829
|
-
yield* harness.provide("hitLimit", command.hitLimit);
|
|
830
|
-
yield* harness.provide("mutantActivation", command.mutantActivation);
|
|
831
|
-
yield* harness.provide("activeMutant", command.activeMutant.id);
|
|
832
|
-
const { rawTests, hasExternalError, externalErrorText } = yield* collectRaw({
|
|
833
|
-
testIds: (() => {
|
|
834
|
-
if (command.testFilter !== void 0) return [...command.testFilter];
|
|
835
|
-
})(),
|
|
836
|
-
relatedFiles: [command.sandboxFileName]
|
|
837
|
-
});
|
|
838
|
-
const hitCount = yield* readHitCount.pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
839
|
-
runnerName: "vitest",
|
|
840
|
-
phase: "mutantRun",
|
|
841
|
-
cause: errorToString(cause)
|
|
842
|
-
})), Effect$1.option, Effect$1.map(Option.getOrUndefined));
|
|
843
|
-
const reportAllKillers = (() => {
|
|
844
|
-
if (typeof input.options.disableBail === "boolean") return input.options.disableBail;
|
|
845
|
-
return false;
|
|
846
|
-
})();
|
|
847
|
-
if (hitCount === void 0) return {
|
|
848
|
-
rawTests,
|
|
849
|
-
projectRoot: input.sandboxDirectory,
|
|
850
|
-
hasExternalError,
|
|
851
|
-
externalErrorText,
|
|
852
|
-
hitLimit: command.hitLimit,
|
|
853
|
-
reportAllKillers
|
|
854
|
-
};
|
|
855
|
-
return {
|
|
856
|
-
rawTests,
|
|
857
|
-
projectRoot: input.sandboxDirectory,
|
|
858
|
-
hasExternalError,
|
|
859
|
-
externalErrorText,
|
|
860
|
-
hitCount,
|
|
861
|
-
hitLimit: command.hitLimit,
|
|
862
|
-
reportAllKillers
|
|
863
|
-
};
|
|
864
|
-
}),
|
|
865
|
-
decode: (raw) => Result.succeed(new VitestMutantRunCommand({
|
|
866
|
-
rawTests: raw.rawTests,
|
|
867
|
-
projectRoot: raw.projectRoot,
|
|
868
|
-
hasExternalError: raw.hasExternalError,
|
|
869
|
-
externalErrorText: raw.externalErrorText,
|
|
870
|
-
hitCount: raw.hitCount,
|
|
871
|
-
hitLimit: raw.hitLimit,
|
|
872
|
-
reportAllKillers: raw.reportAllKillers
|
|
873
|
-
})),
|
|
874
|
-
decide: interpretVitestRun,
|
|
875
|
-
encode: (outcome) => Result.match(outcome, {
|
|
876
|
-
onFailure: (e) => ({
|
|
877
|
-
status: "error",
|
|
878
|
-
errorMessage: e.message
|
|
879
|
-
}),
|
|
880
|
-
onSuccess: (out) => {
|
|
881
|
-
const nrOfTests = () => countIdRecords(parseJson(out.testsJson));
|
|
882
|
-
return Match.value(out).pipe(Match.tag("Error", (error) => ({
|
|
883
|
-
status: "error",
|
|
884
|
-
errorMessage: error.errorMessage ?? "unknown"
|
|
885
|
-
})), Match.tag("Timeout", (timeout) => (() => {
|
|
886
|
-
if (timeout.reason === void 0) return { status: "timeout" };
|
|
887
|
-
return {
|
|
888
|
-
status: "timeout",
|
|
889
|
-
reason: timeout.reason
|
|
890
|
-
};
|
|
891
|
-
})()), Match.tag("Killed", (killed) => ({
|
|
892
|
-
status: "killed",
|
|
893
|
-
failureMessage: killed.failureMessage ?? "",
|
|
894
|
-
killedBy: (() => {
|
|
895
|
-
if (killed.killerIds !== void 0) return [...killed.killerIds];
|
|
896
|
-
return [];
|
|
897
|
-
})(),
|
|
898
|
-
nrOfTests: nrOfTests()
|
|
899
|
-
})), Match.tag("Survived", () => ({
|
|
900
|
-
status: "survived",
|
|
901
|
-
nrOfTests: nrOfTests()
|
|
902
|
-
})), Match.exhaustive);
|
|
903
|
-
}
|
|
904
|
-
}),
|
|
905
|
-
write: (output, _raw) => Effect$1.succeed(output)
|
|
906
|
-
});
|
|
907
|
-
const dryRunFilter = (options) => {
|
|
908
|
-
const relatedFiles = Option.getOrUndefined(Option.map(Option.fromNullishOr(options.files), (files) => [...files]));
|
|
909
|
-
return Match.value(testFilesProvided(options)).pipe(Match.when(true, () => ({
|
|
910
|
-
testFiles: Option.getOrElse(Option.map(Option.fromNullishOr(options.testFiles), (files) => [...files]), () => []),
|
|
911
|
-
relatedFiles
|
|
912
|
-
})), Match.orElse(() => ({ relatedFiles })));
|
|
913
|
-
};
|
|
914
|
-
const completeDryRun = (testsJson) => Effect$1.gen(function* () {
|
|
915
|
-
const tests = Match.value(parseJson(testsJson)).pipe(Match.when(Array.isArray, (entries) => entries.filter(isTestResultLike)), Match.orElse(() => []));
|
|
916
|
-
const mutantCoverage = yield* readMutantCoverage.pipe(Effect$1.mapError((cause) => new TestRunnerFailed({
|
|
917
|
-
runnerName: "vitest",
|
|
918
|
-
phase: "dryRun",
|
|
919
|
-
cause: errorToString(cause)
|
|
920
|
-
})));
|
|
921
|
-
return Match.value(mutantCoverage).pipe(Match.when(Match.defined, (coverage) => ({
|
|
922
|
-
status: "complete",
|
|
923
|
-
tests,
|
|
924
|
-
mutantCoverage: coverage
|
|
925
|
-
})), Match.orElse(() => ({
|
|
926
|
-
status: "complete",
|
|
927
|
-
tests
|
|
928
|
-
})));
|
|
929
|
-
});
|
|
930
|
-
const dryRun = (options) => Effect$1.gen(function* () {
|
|
931
|
-
yield* (yield* VitestHarness).setMode("dry-run");
|
|
932
|
-
const filter = dryRunFilter(options);
|
|
933
|
-
const { rawTests, hasExternalError, externalErrorText } = yield* collectRaw(filter);
|
|
934
|
-
const decision = decideVitestDryRun(new VitestDryRunCommand({
|
|
935
|
-
rawTests,
|
|
936
|
-
projectRoot: input.sandboxDirectory,
|
|
937
|
-
hasExternalError,
|
|
938
|
-
externalErrorText
|
|
939
|
-
}));
|
|
940
|
-
return yield* Match.value(decision).pipe(Match.tag("Error", (error) => Effect$1.succeed({
|
|
941
|
-
status: "error",
|
|
942
|
-
errorMessage: error.errorMessage
|
|
943
|
-
})), Match.tag("Complete", (complete) => completeDryRun(complete.testsJson)), Match.exhaustive);
|
|
944
|
-
}).pipe(Effect$1.provideService(VitestHarness, harnessImpl), Effect$1.mapError((cause) => (() => {
|
|
945
|
-
if (cause instanceof TestRunnerFailed) return cause;
|
|
946
|
-
return new TestRunnerFailed({
|
|
947
|
-
runnerName: "vitest",
|
|
948
|
-
phase: "dryRun",
|
|
949
|
-
cause: errorToString(cause)
|
|
950
|
-
});
|
|
951
|
-
})()));
|
|
952
|
-
const mutantRun = (options) => mutantRunCell.run(options).pipe(Effect$1.provideService(VitestHarness, harnessImpl), Effect$1.mapError((cause) => (() => {
|
|
953
|
-
if (cause instanceof TestRunnerFailed) return cause;
|
|
954
|
-
return new TestRunnerFailed({
|
|
955
|
-
runnerName: "vitest",
|
|
956
|
-
phase: "mutantRun",
|
|
957
|
-
cause: errorToString(cause)
|
|
958
|
-
});
|
|
959
|
-
})()));
|
|
960
|
-
const disposeContext = (ctx, localSetupFile) => Effect$1.gen(function* () {
|
|
961
|
-
Option.match(Option.fromNullishOr(localSetupFile), {
|
|
962
|
-
onNone: () => void 0,
|
|
963
|
-
onSome: (file) => {
|
|
964
|
-
ctx.onClose(() => Effect$1.runPromise(fsService.remove(file, {
|
|
965
|
-
recursive: true,
|
|
966
|
-
force: true
|
|
967
|
-
}).pipe(Effect$1.orElseSucceed(() => void 0))));
|
|
968
|
-
}
|
|
969
|
-
});
|
|
970
|
-
yield* Effect$1.tryPromise({
|
|
971
|
-
try: () => ctx.close(),
|
|
972
|
-
catch: (cause) => new TestRunnerFailed({
|
|
973
|
-
runnerName: "vitest",
|
|
974
|
-
phase: "dispose",
|
|
975
|
-
cause: errorToString(cause)
|
|
976
|
-
})
|
|
977
|
-
});
|
|
978
|
-
});
|
|
979
|
-
const dispose = Effect$1.gen(function* () {
|
|
980
|
-
const state = yield* getState;
|
|
981
|
-
return yield* Option.match(Option.fromNullishOr(state.ctx), {
|
|
982
|
-
onNone: () => Effect$1.void,
|
|
983
|
-
onSome: (ctx) => disposeContext(ctx, state.localSetupFile)
|
|
984
|
-
});
|
|
985
|
-
});
|
|
986
|
-
return TestRunner.of({
|
|
987
|
-
capabilities,
|
|
988
|
-
init,
|
|
989
|
-
dryRun,
|
|
990
|
-
mutantRun,
|
|
991
|
-
dispose
|
|
992
|
-
});
|
|
993
|
-
}));
|
|
994
|
-
function isTestResultLike(value) {
|
|
995
|
-
return Predicate.isObject(value) && typeof value["id"] === "string";
|
|
996
|
-
}
|
|
997
|
-
function countIdRecords(raw) {
|
|
998
|
-
return Match.value(raw).pipe(Match.when(Array.isArray, (entries) => entries.filter(isTestResultLike).length), Match.orElse(() => 0));
|
|
999
|
-
}
|
|
1000
|
-
const mergeHitCount = (to, mutantId, hitCount) => Option.match(Option.fromNullishOr(to[mutantId]), {
|
|
1001
|
-
onNone: () => {
|
|
1002
|
-
to[mutantId] = hitCount;
|
|
1003
|
-
},
|
|
1004
|
-
onSome: (existing) => {
|
|
1005
|
-
to[mutantId] = existing + hitCount;
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
function mergeCoverage(to, from) {
|
|
1009
|
-
for (const [mutantId, hitCount] of Object.entries(from)) mergeHitCount(to, mutantId, hitCount);
|
|
1010
|
-
}
|
|
1011
|
-
//#endregion
|
|
1012
1
|
//#region src/index.ts
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
* provide it fails to compile.
|
|
1019
|
-
*/
|
|
1020
|
-
const strykerPlugins = [declarePlugin("TestRunner", "vitest", Layer.unwrap(Effect$1.gen(function* () {
|
|
1021
|
-
const options = yield* RunConfiguration;
|
|
1022
|
-
const sandboxDirectory = yield* SandboxDirectory;
|
|
1023
|
-
return makeVitestRunnerLayer({
|
|
1024
|
-
options,
|
|
1025
|
-
sandboxDirectory
|
|
1026
|
-
});
|
|
1027
|
-
})))];
|
|
1028
|
-
/**
|
|
1029
|
-
* The `vitest` option section as a JSON Schema document, for Stryker's option
|
|
1030
|
-
* validation — derived from the declaration, never read from a file. It is built
|
|
1031
|
-
* here, at the entry that contributes it, because a document is a *use* of a
|
|
1032
|
-
* schema and the schema module exports only declarations.
|
|
1033
|
-
*/
|
|
1034
|
-
const strykerValidationSchema = S.toJsonSchemaDocument(S.Struct({ vitest: S.optional(VitestRunnerOptionsSchema) })).schema;
|
|
2
|
+
const strykerPlugins = [{
|
|
3
|
+
kind: "TestRunner",
|
|
4
|
+
name: "vitest",
|
|
5
|
+
workerEntry: new URL("./main.mjs", import.meta.url).href
|
|
6
|
+
}];
|
|
1035
7
|
//#endregion
|
|
1036
|
-
export { strykerPlugins
|
|
8
|
+
export { strykerPlugins };
|