@systemfsoftware/vitest 0.1.0 → 0.3.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 +24 -0
- package/dist/engine-ZbxyPNiN.d.ts +166 -0
- package/dist/failure-DC_SJoxW.mjs +1293 -0
- package/dist/failure.d.ts +204 -0
- package/dist/failure.mjs +4 -0
- package/dist/index.d.ts +3 -154
- package/dist/index.mjs +56 -529
- package/dist/integration.mjs +1 -1
- package/dist/{test-context-hvSoKLtw.mjs → test-context-D_-acRSc.mjs} +122 -5
- package/package.json +17 -4
|
@@ -2,8 +2,112 @@
|
|
|
2
2
|
import { a as booleanRefusal, b as refusedText, c as negatedText, m as refusePiecewise, s as neededText, t as authorize, u as refusalOf } from "./guard-BocOhXt2.mjs";
|
|
3
3
|
import * as V from "vitest";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Function from "effect/Function";
|
|
5
6
|
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
6
8
|
import * as Context from "effect/Context";
|
|
9
|
+
//#region src/internal/call-site.ts
|
|
10
|
+
/**
|
|
11
|
+
* The one rule that reads a frame out of a stack: which frame a failure is raised from, and which frame an author
|
|
12
|
+
* wrote the thing that raised it on (R2, KTD6).
|
|
13
|
+
*
|
|
14
|
+
* A deferred check and a declared property both fail inside the fork, where the stack holds only the fork's frames
|
|
15
|
+
* and the author's line is long gone. Each captures its call site while the author's frame is still on the stack
|
|
16
|
+
* and hands it to the failure as its first frame, so the renderer's R2 walk leads with the author's line.
|
|
17
|
+
*
|
|
18
|
+
* The rule is the same everywhere it applies: the first frame outside the vendored install, node internals, and
|
|
19
|
+
* every spec library's own `src` and `dist` — {@link LIBRARY_DIRS}. A caller may name its own extra frames with a
|
|
20
|
+
* {@link RegExp}, and a value that captured a raw stack records it whole, resolved here rather than by a second
|
|
21
|
+
* copy of the rule. `effect-spec-runtime`, `effect-cell-types`, `effect-gherkin-spec` and `trace-spec` read it from
|
|
22
|
+
* here.
|
|
23
|
+
*
|
|
24
|
+
* @since 4.0.0
|
|
25
|
+
*/
|
|
26
|
+
const FRAME = /\(?((?:file:\/\/)?[^()\s]+):(\d+):(\d+)\)?\s*$/u;
|
|
27
|
+
const FILE_PROTOCOL = "file://";
|
|
28
|
+
const FRAME_AT = "at ";
|
|
29
|
+
const FRAME_PAREN = " (";
|
|
30
|
+
const and = (left, right) => left && right;
|
|
31
|
+
const not = (value) => !value;
|
|
32
|
+
const LIBRARY_DIRS = [
|
|
33
|
+
"runner/vitest",
|
|
34
|
+
"effect-spec-runtime",
|
|
35
|
+
"effect-cell-types",
|
|
36
|
+
"effect-gherkin-spec",
|
|
37
|
+
"storybook-gherkin",
|
|
38
|
+
"conformance-spec",
|
|
39
|
+
"differential-spec",
|
|
40
|
+
"trace-spec",
|
|
41
|
+
"effect-daemon-spec"
|
|
42
|
+
];
|
|
43
|
+
const isVendoredPath = (path) => path.includes("node_modules") || path.startsWith("node:");
|
|
44
|
+
const inOwnDir = (path) => (dir) => path.includes(`/${dir}/src/`) || path.includes(`/${dir}/dist/`);
|
|
45
|
+
const isLibraryPath = (path) => LIBRARY_DIRS.some(inOwnDir(path));
|
|
46
|
+
const isUserPath = (path) => and(not(isVendoredPath(path)), not(isLibraryPath(path)));
|
|
47
|
+
const stripProtocol = (path) => path.startsWith(FILE_PROTOCOL) ? path.slice(7) : path;
|
|
48
|
+
const isFrame = (frame) => frame !== void 0;
|
|
49
|
+
const partsOf = (match) => Option.flatMap(Option.fromNullishOr(match), (found) => Option.map(Option.all([
|
|
50
|
+
Option.fromNullishOr(found[1]),
|
|
51
|
+
Option.fromNullishOr(found[2]),
|
|
52
|
+
Option.fromNullishOr(found[3])
|
|
53
|
+
]), ([path, line, column]) => ({
|
|
54
|
+
path: stripProtocol(path),
|
|
55
|
+
line,
|
|
56
|
+
column
|
|
57
|
+
})));
|
|
58
|
+
const beforeParen = (rest) => Option.flatMap(Option.fromNullishOr(rest.indexOf(FRAME_PAREN)), (paren) => Option.some(rest.slice(0, paren)));
|
|
59
|
+
const afterAt = (line) => line.startsWith(FRAME_AT) ? Option.some(line.slice(3)) : Option.none();
|
|
60
|
+
const nonEmpty = (text) => text.length > 0;
|
|
61
|
+
const fnIn = (line) => Option.getOrUndefined(Option.flatMap(afterAt(line), (rest) => Option.filter(beforeParen(rest), nonEmpty)));
|
|
62
|
+
const frameOfLine = (line) => {
|
|
63
|
+
const raw = line.trim();
|
|
64
|
+
return Option.match(partsOf(FRAME.exec(raw)), {
|
|
65
|
+
onNone: () => void 0,
|
|
66
|
+
onSome: (parts) => ({
|
|
67
|
+
...parts,
|
|
68
|
+
fn: fnIn(raw),
|
|
69
|
+
raw
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
/** @internal */
|
|
74
|
+
const framesOf = (stack) => stack.split("\n").map(frameOfLine).filter(isFrame);
|
|
75
|
+
/** @internal */
|
|
76
|
+
const siteOfFrame = (frame) => `${frame.path}:${frame.line}:${frame.column}`;
|
|
77
|
+
/** @internal */
|
|
78
|
+
const isUserFrame = (frame) => isUserPath(frame.path);
|
|
79
|
+
const NO_LIBRARY = /$^/u;
|
|
80
|
+
const currentStack = () => (/* @__PURE__ */ new Error()).stack ?? "";
|
|
81
|
+
const isSiteOf = (library) => (frame) => and(isUserFrame(frame), not(library.test(frame.raw)));
|
|
82
|
+
const firstFrameOf = (stack, library) => framesOf(stack).find(isSiteOf(library));
|
|
83
|
+
/** @internal */
|
|
84
|
+
const callFrameOutside = (library) => {
|
|
85
|
+
const frame = firstFrameOf(currentStack(), library);
|
|
86
|
+
return frame === void 0 ? void 0 : frame.raw;
|
|
87
|
+
};
|
|
88
|
+
/** @internal */
|
|
89
|
+
const callFrame = () => callFrameOutside(NO_LIBRARY);
|
|
90
|
+
const callSiteOutside = (library) => {
|
|
91
|
+
const frame = firstFrameOf(currentStack(), library);
|
|
92
|
+
return frame === void 0 ? void 0 : siteOfFrame(frame);
|
|
93
|
+
};
|
|
94
|
+
/** @internal */
|
|
95
|
+
const callSite = () => callSiteOutside(NO_LIBRARY);
|
|
96
|
+
const firstLineOf = (lines) => lines[0] ?? "";
|
|
97
|
+
const stackOf = (error) => error.stack ?? "";
|
|
98
|
+
const withRaisingFrameImpl = (error, frame) => {
|
|
99
|
+
if (frame === void 0) return error;
|
|
100
|
+
const lines = stackOf(error).split("\n");
|
|
101
|
+
error.stack = [
|
|
102
|
+
firstLineOf(lines),
|
|
103
|
+
frame,
|
|
104
|
+
...lines.slice(1)
|
|
105
|
+
].join("\n");
|
|
106
|
+
return error;
|
|
107
|
+
};
|
|
108
|
+
/** @internal */
|
|
109
|
+
const withRaisingFrame = Function.dual(2, withRaisingFrameImpl);
|
|
110
|
+
//#endregion
|
|
7
111
|
//#region src/internal/checks.ts
|
|
8
112
|
/**
|
|
9
113
|
* The checks a test receives as its callback parameter. There is no importable `expect`: the only one in reach is
|
|
@@ -42,11 +146,12 @@ const forView = (view, negated) => ({
|
|
|
42
146
|
negated
|
|
43
147
|
});
|
|
44
148
|
const notView = (view) => forView(view, !view.negated);
|
|
45
|
-
const viewFor = (written, actual, message) => ({
|
|
149
|
+
const viewFor = (written, actual, message, site) => ({
|
|
46
150
|
actual,
|
|
47
151
|
message,
|
|
48
152
|
negated: false,
|
|
49
|
-
written
|
|
153
|
+
written,
|
|
154
|
+
site
|
|
50
155
|
});
|
|
51
156
|
const messageOf = (argument) => typeof argument === "string" ? argument : void 0;
|
|
52
157
|
const refuseBooleanActual = (actual) => typeof actual === "boolean";
|
|
@@ -73,11 +178,23 @@ const invokeMember = (target, property, args) => {
|
|
|
73
178
|
const member = memberAt(target, property);
|
|
74
179
|
if (isCallable(member)) Reflect.apply(member, target, args);
|
|
75
180
|
};
|
|
76
|
-
const
|
|
181
|
+
const judgedBy = (view, property, args, make) => {
|
|
77
182
|
const assertion = make(view.actual, view.message);
|
|
78
183
|
const target = view.negated ? assertion.not : assertion;
|
|
79
184
|
invokeMember(target, property, args);
|
|
80
185
|
};
|
|
186
|
+
const raisedFailure = (thrown, site) => thrown instanceof Error ? withRaisingFrame(thrown, site) : thrown;
|
|
187
|
+
/**
|
|
188
|
+
* A failed judgement is a failure the fork raised: by now the stack holds only the fork's frames, so the site the
|
|
189
|
+
* body called `expect` from is prepended to the error's stack (R2, KTD6).
|
|
190
|
+
*/
|
|
191
|
+
const judgementOf = (view, property, args) => (make) => {
|
|
192
|
+
try {
|
|
193
|
+
judgedBy(view, property, args, make);
|
|
194
|
+
} catch (thrown) {
|
|
195
|
+
throw raisedFailure(thrown, view.site);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
81
198
|
const judgedCheck = (view, property, args) => makeCheck(judgementOf(view, property, args), view.written);
|
|
82
199
|
const genericMember = (view, property) => (...args) => judgedCheck(view, property, args);
|
|
83
200
|
const neededMember = (view, property) => (...args) => {
|
|
@@ -117,7 +234,7 @@ const viewHandler = (view) => ({ get: (_target, property) => memberFor(view, pro
|
|
|
117
234
|
const matcherView = (view) => new Proxy({}, viewHandler(view));
|
|
118
235
|
const assertionView = (written, args) => {
|
|
119
236
|
if (refuseBooleanActual(args[0])) throw refusalOf(booleanRefusal);
|
|
120
|
-
return matcherView(viewFor(written, args[0], messageOf(args[1])));
|
|
237
|
+
return matcherView(viewFor(written, args[0], messageOf(args[1]), callFrame()));
|
|
121
238
|
};
|
|
122
239
|
const isDecodingSchema = (value) => Schema.isSchema(value);
|
|
123
240
|
const asMatcher = (value) => {
|
|
@@ -203,4 +320,4 @@ const vitestTestContextKey = "@systemfsoftware/vitest/TestContext";
|
|
|
203
320
|
*/
|
|
204
321
|
const VitestTestContext = Context.Reference(vitestTestContextKey, { defaultValue: () => null });
|
|
205
322
|
//#endregion
|
|
206
|
-
export { isCheck as a, checksFor as i, vitestTestContextKey as n, makeLedger as o, Asserted as r, VitestTestContext as t };
|
|
323
|
+
export { isCheck as a, callFrameOutside as c, isUserFrame as d, siteOfFrame as f, checksFor as i, callSite as l, vitestTestContextKey as n, makeLedger as o, withRaisingFrame as p, Asserted as r, callFrame as s, VitestTestContext as t, framesOf as u };
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemfsoftware/vitest",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/systemfsoftware/systemfsoftware.git",
|
|
9
|
-
"directory": "packages/vitest"
|
|
9
|
+
"directory": "packages/runner/vitest"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/vitest#readme",
|
|
11
|
+
"homepage": "https://github.com/systemfsoftware/systemfsoftware/tree/main/packages/runner/vitest#readme",
|
|
12
12
|
"bugs": "https://github.com/systemfsoftware/systemfsoftware/issues",
|
|
13
13
|
"description": "A fork of @effect/vitest whose defaults make the lazy test a good test: expect bound to the test's own callback, one check per observed state, fresh layers, a second run that catches leaked state, virtual time, and properties refuted against a constant impostor.",
|
|
14
14
|
"keywords": [
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"types": "./dist/index.d.ts",
|
|
26
26
|
"default": "./dist/index.mjs"
|
|
27
27
|
},
|
|
28
|
+
"./failure": {
|
|
29
|
+
"types": "./dist/failure.d.ts",
|
|
30
|
+
"default": "./dist/failure.mjs"
|
|
31
|
+
},
|
|
28
32
|
"./guard": {
|
|
29
33
|
"types": "./dist/guard.d.ts",
|
|
30
34
|
"default": "./dist/guard.mjs"
|
|
@@ -44,7 +48,14 @@
|
|
|
44
48
|
],
|
|
45
49
|
"devDependencies": {
|
|
46
50
|
"@systemfsoftware/arethetypeswrong-cli": "^4.2.0",
|
|
47
|
-
"@systemfsoftware/oxlint-config-recommended": "^3.
|
|
51
|
+
"@systemfsoftware/oxlint-config-recommended": "^3.3.0",
|
|
52
|
+
"@systemfsoftware/stryker-config": "^0.1.0",
|
|
53
|
+
"@systemfsoftware/stryker-ignorer-effect-schema-declarations": "latest",
|
|
54
|
+
"@systemfsoftware/stryker-ignorer-in-source-vitest-block": "latest",
|
|
55
|
+
"@systemfsoftware/stryker-js": "latest",
|
|
56
|
+
"@systemfsoftware/stryker-js-typescript-checker": "latest",
|
|
57
|
+
"@systemfsoftware/stryker-js-vitest-runner": "latest",
|
|
58
|
+
"@systemfsoftware/stryker-test-contribution": "latest",
|
|
48
59
|
"@systemfsoftware/tsconfig": "^2.0.1",
|
|
49
60
|
"@systemfsoftware/tsdown-config": "^0.1.0",
|
|
50
61
|
"@systemfsoftware/vitest-config": "^0.1.0",
|
|
@@ -61,6 +72,7 @@
|
|
|
61
72
|
"vitest": "^5"
|
|
62
73
|
},
|
|
63
74
|
"publishConfig": {
|
|
75
|
+
"access": "public",
|
|
64
76
|
"provenance": true
|
|
65
77
|
},
|
|
66
78
|
"scripts": {
|
|
@@ -68,6 +80,7 @@
|
|
|
68
80
|
"build": "tsdown -l warn",
|
|
69
81
|
"typecheck": "tsc -b",
|
|
70
82
|
"test": "vitest run",
|
|
83
|
+
"mutation": "stryker run",
|
|
71
84
|
"test:types": "TSTYCHE_TYPESCRIPT_MODULE=tstyche-typescript tstyche",
|
|
72
85
|
"lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
|
|
73
86
|
"attw": "attw --pack ."
|