@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.
@@ -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 judgementOf = (view, property, args) => (make) => {
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.1.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.1.0",
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 ."