@systemfsoftware/vitest 0.0.0 → 0.1.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 +6 -0
- package/README.md +113 -61
- package/dist/TestClock.d.ts +1 -1
- package/dist/checks-DiuzHhQM.d.ts +232 -0
- package/dist/guard-BocOhXt2.mjs +246 -0
- package/dist/guard.d.ts +2 -0
- package/dist/guard.mjs +19 -0
- package/dist/index.d.ts +201 -287
- package/dist/index.mjs +353 -562
- package/dist/integration.d.ts +37 -0
- package/dist/integration.mjs +38 -0
- package/dist/test-context-hvSoKLtw.mjs +206 -0
- package/package.json +9 -9
- package/dist/refusals-BQtDszyU.mjs +0 -32
- package/dist/refusals-_6e3GzVK.d.ts +0 -37
- package/dist/refusals.d.ts +0 -3
- package/dist/refusals.mjs +0 -3
- package/dist/utils.d.ts +0 -197
- package/dist/utils.mjs +0 -227
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,96 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { _ as refuseScopedLiveLane, d as refuseAsyncBody, f as refuseEffectLane, g as refuseScopedLane, i as markTask, l as noCheckRefusal, o as isRefusal$1, p as refuseHook, u as refusalOf$1, v as refuseSyncBody, x as unprovidedText, y as refuseUnyielded } from "./guard-BocOhXt2.mjs";
|
|
3
|
+
import { a as isCheck, i as checksFor, n as vitestTestContextKey$1, o as makeLedger, r as Asserted, t as VitestTestContext$1 } from "./test-context-hvSoKLtw.mjs";
|
|
3
4
|
import * as V from "vitest";
|
|
4
|
-
import { inject } from "vitest";
|
|
5
|
-
import * as Function from "effect/Function";
|
|
6
|
-
import * as Context from "effect/Context";
|
|
7
|
-
import * as Fiber from "effect/Fiber";
|
|
8
|
-
import * as Equal from "effect/Equal";
|
|
9
|
-
import { Schema } from "effect";
|
|
10
|
-
import * as Effect from "effect/Effect";
|
|
5
|
+
import { afterAll, assertType, beforeAll, expectTypeOf, inject, inject as inject$1, onTestFailed, onTestFinished, recordArtifact, vi, vitest } from "vitest";
|
|
11
6
|
import * as Cause from "effect/Cause";
|
|
12
7
|
import * as Duration from "effect/Duration";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
13
9
|
import * as Exit from "effect/Exit";
|
|
10
|
+
import * as Function from "effect/Function";
|
|
14
11
|
import * as Layer from "effect/Layer";
|
|
15
12
|
import * as Schedule from "effect/Schedule";
|
|
16
|
-
import * as Scheduler from "effect/Scheduler";
|
|
17
13
|
import * as Schema$1 from "effect/Schema";
|
|
18
14
|
import * as Scope from "effect/Scope";
|
|
19
15
|
import * as TestClock from "effect/testing/TestClock";
|
|
20
16
|
import * as TestConsole from "effect/testing/TestConsole";
|
|
17
|
+
import * as Equal from "effect/Equal";
|
|
18
|
+
import { Schema } from "effect";
|
|
21
19
|
import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary";
|
|
22
20
|
import * as Data from "effect/Data";
|
|
23
21
|
import * as Clock from "effect/Clock";
|
|
24
|
-
|
|
25
|
-
//#region src/internal/
|
|
22
|
+
import * as Scheduler from "effect/Scheduler";
|
|
23
|
+
//#region src/internal/property/defaults.ts
|
|
24
|
+
/** @internal The `test.provide` key the shared config fills with this run's tier's check options. */
|
|
25
|
+
const checkDefaultsKey = "@systemfsoftware/vitest:property-check";
|
|
26
|
+
/** @internal The configured property-check defaults, or `undefined` when the run provided none. */
|
|
27
|
+
const providedCheckDefaults = () => inject$1(checkDefaultsKey);
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/internal/driver.ts
|
|
26
30
|
/**
|
|
27
|
-
* The
|
|
28
|
-
* `Fiber.getCurrent`, so no node-only store is needed (KTD4).
|
|
31
|
+
* The driver: one Effect that steps the body's generator in the fiber the test runs in.
|
|
29
32
|
*
|
|
30
|
-
*
|
|
33
|
+
* A `yield*` is a step, so the runner sees every one of them (KTD1):
|
|
34
|
+
* - a branded check is judged against this test's ledger, so it counts, and a failed judgement fails the check
|
|
35
|
+
* itself and stops the test right there (R8);
|
|
36
|
+
* - anything else opens a new observed state and then runs, which is what makes a second check on one state a
|
|
37
|
+
* second check in one window (R5), and a check inside a loop the same window as its siblings.
|
|
38
|
+
*
|
|
39
|
+
* The steps run in the same fiber as the test, so layers, virtual time, concurrency, the second run and scope
|
|
40
|
+
* finalizers keep working through the runner unchanged. A step that fails, and the test being interrupted, both
|
|
41
|
+
* return the generator: its `finally` blocks run and the steps after it never start (R8). When the body returns,
|
|
42
|
+
* the gate refuses what the body wrote and never judged, then a body that judged nothing at all (R4).
|
|
31
43
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
/**
|
|
45
|
+
* A value the body yielded, as the driver runs it. What a body needs of its environment belongs to the layer the
|
|
46
|
+
* runner provides around this whole Effect, so the driver erases the channels here, exactly as the runner erases
|
|
47
|
+
* a body's own (`outputIsEffect` in `runner.ts`): the layer block is where a requirement is checked, and a body
|
|
48
|
+
* that needs a service nothing provides is refused by the runner at run time (R9).
|
|
49
|
+
*/
|
|
50
|
+
const isRunnable = (value) => Effect.isEffect(value);
|
|
51
|
+
/** A body that yielded something the driver cannot run ran nothing, so nothing in the test could fail. */
|
|
52
|
+
const refuseUnsteppable = "✗ the body yielded a value that is not an Effect. Yield the Effect itself, or a check: yield* expect(actual).toEqual(expected).";
|
|
53
|
+
/**
|
|
54
|
+
* A yielded Effect: a branded check judges itself, and anything else opens a new observed state before it runs,
|
|
55
|
+
* which is what resets the ledger's one-check window (R5).
|
|
56
|
+
*/
|
|
57
|
+
const runEffect = (value, ledger) => isCheck(value) ? value : Effect.andThen(Effect.sync(() => ledger.asserted.step()), value);
|
|
58
|
+
/** One step: anything the driver cannot run is refused, because then no step of the test ran at all. */
|
|
59
|
+
const runStep = (value, ledger) => isRunnable(value) ? runEffect(value, ledger) : Effect.die(refusalOf$1(refuseUnsteppable));
|
|
60
|
+
/** Steps the iterator to its end, feeding each yielded Effect's result back into the body. */
|
|
61
|
+
const stepThrough = (steps, ledger, input) => Effect.suspend(() => {
|
|
62
|
+
const state = steps.next(input);
|
|
63
|
+
if (state.done === true) return Effect.void;
|
|
64
|
+
return Effect.flatMap(runStep(state.value, ledger), (value) => stepThrough(steps, ledger, value));
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* After the body returns: a check written and never yielded is refused first, then a body that judged none.
|
|
68
|
+
* Both carry the same text the refusal carries on its compile channel, branded by `refusalOf` so the runner's
|
|
69
|
+
* second run re-throws them as the refusals they are.
|
|
70
|
+
*/
|
|
71
|
+
const gate = (ledger) => Effect.suspend(() => ledger.unyielded() > 0 ? refuse(refuseUnyielded) : judgedNone(ledger));
|
|
72
|
+
const judgedNone = (ledger) => ledger.judged() === 0 ? refuse(noCheckRefusal) : Effect.void;
|
|
73
|
+
const refuse = (text) => Effect.die(refusalOf$1(text));
|
|
74
|
+
/**
|
|
75
|
+
* A failure or an interruption returns the generator, so its `finally` blocks run and the step after the failed
|
|
76
|
+
* check never does (R8).
|
|
77
|
+
*/
|
|
78
|
+
const unwind = (steps) => Effect.sync(() => {
|
|
79
|
+
steps.return?.(void 0);
|
|
80
|
+
});
|
|
81
|
+
const driveBody = (body, args, ledger) => Effect.suspend(() => {
|
|
82
|
+
const steps = body(...args);
|
|
83
|
+
return Effect.onExit(Effect.flatMap(stepThrough(steps, ledger, void 0), () => gate(ledger)), (exit) => Exit.isSuccess(exit) ? Effect.void : unwind(steps));
|
|
45
84
|
});
|
|
85
|
+
/** The body's steps, with this test's ledger provided as the `Asserted` every check in it reaches for. */
|
|
86
|
+
const driven = (body, args, ledger) => Effect.provideService(driveBody(body, args, ledger), Asserted, ledger.asserted);
|
|
46
87
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
88
|
+
* Runs one test's body: `body` called with `args`, stepped against `ledger`, with the ledger's `asserted`
|
|
89
|
+
* provided as `Asserted` so every check in the body — including one yielded inside a forked fiber — reaches it.
|
|
49
90
|
*
|
|
50
91
|
* @internal
|
|
51
92
|
*/
|
|
52
|
-
const
|
|
53
|
-
const NO_ERRORS = [];
|
|
54
|
-
/** @internal */
|
|
55
|
-
const errorCount = (ctx) => presentErrors(ctx.task.result?.errors).length;
|
|
56
|
-
const presentErrors = (errors) => errors === void 0 ? NO_ERRORS : errors;
|
|
93
|
+
const drive = Function.dual(3, driven);
|
|
57
94
|
//#endregion
|
|
58
95
|
//#region src/internal/equal.ts
|
|
59
96
|
let registered = false;
|
|
@@ -81,12 +118,6 @@ var LeakedState = class extends Schema.TaggedError()("LeakedState", { detail: Sc
|
|
|
81
118
|
}
|
|
82
119
|
};
|
|
83
120
|
/** @internal */
|
|
84
|
-
var AfterFailedExpect = class extends Schema.TaggedError()("AfterFailedExpect", { detail: Schema.String }) {
|
|
85
|
-
get message() {
|
|
86
|
-
return this.detail;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
/** @internal */
|
|
90
121
|
var Slop = class extends Schema.TaggedError()("Slop", { detail: Schema.String }) {
|
|
91
122
|
get message() {
|
|
92
123
|
return this.detail;
|
|
@@ -99,120 +130,6 @@ var InvalidBudget = class extends Schema.TaggedError()("InvalidBudget", { detail
|
|
|
99
130
|
}
|
|
100
131
|
};
|
|
101
132
|
//#endregion
|
|
102
|
-
//#region src/internal/owned.ts
|
|
103
|
-
/** @internal */
|
|
104
|
-
const OwnedAssertions = Context.Reference("effect-vitest/owned", { defaultValue: () => false });
|
|
105
|
-
/** @internal */
|
|
106
|
-
const isOwned = () => Fiber.getCurrent()?.getRef(OwnedAssertions) === true;
|
|
107
|
-
/** @internal */
|
|
108
|
-
const owned$1 = (self) => Effect.provideService(self, OwnedAssertions, true);
|
|
109
|
-
/**
|
|
110
|
-
* Counts an Effect-native check as an assertion of the run it happens in, so the run's own gate sees it.
|
|
111
|
-
*
|
|
112
|
-
* @internal
|
|
113
|
-
*/
|
|
114
|
-
const recordAssertion$1 = () => {
|
|
115
|
-
const run = lookupRun();
|
|
116
|
-
if (run !== void 0) bump(run);
|
|
117
|
-
};
|
|
118
|
-
const bump = (run) => {
|
|
119
|
-
const { expect } = run.ctx;
|
|
120
|
-
expect.setState({ assertionCalls: expect.getState().assertionCalls + 1 });
|
|
121
|
-
};
|
|
122
|
-
const bindingOf = (running, owned) => ({ bind: (self) => self.pipe(Effect.provideService(Running, running), Effect.provideService(OwnedAssertions, owned)) });
|
|
123
|
-
/**
|
|
124
|
-
* The run binding of the test the caller is executing in.
|
|
125
|
-
*
|
|
126
|
-
* @internal
|
|
127
|
-
*/
|
|
128
|
-
const captureRunBinding$1 = Effect.withFiber((fiber) => Effect.succeed(bindingOf(fiber.getRef(Running), fiber.getRef(OwnedAssertions))));
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/internal/expect.ts
|
|
131
|
-
registerEqualTester();
|
|
132
|
-
const refuse = (detail) => {
|
|
133
|
-
throw new Slop({ detail });
|
|
134
|
-
};
|
|
135
|
-
const narrowedNames = (negated) => negated ? narrowNegated : narrowVacuous;
|
|
136
|
-
const narrowedName = (property, negated) => namedMatch(property, narrowedNames(negated));
|
|
137
|
-
const namedMatch = (property, names) => typeof property === "string" ? foundMatch(property, names) : void 0;
|
|
138
|
-
const foundMatch = (property, names) => names.find((name) => name === property);
|
|
139
|
-
const refuseNarrowed = (name) => () => refuse(presenceMessage(name));
|
|
140
|
-
const refusedMember = (property, negated) => {
|
|
141
|
-
const name = narrowedName(property, negated);
|
|
142
|
-
return name === void 0 ? void 0 : refuseNarrowed(name);
|
|
143
|
-
};
|
|
144
|
-
const memberAt = (target, property) => Reflect.get(target, property, target);
|
|
145
|
-
/**
|
|
146
|
-
* The narrowed names answer with the refusal, `not` re-enters the guard with the negation flipped, and every
|
|
147
|
-
* other member is Vitest's own, handed over unbound so Chai's property accessors keep working.
|
|
148
|
-
*/
|
|
149
|
-
const guardOf = (assertion, negated) => new Proxy(assertion, guardHandler(negated));
|
|
150
|
-
const guardHandler = (negated) => ({ get: (target, property) => guardedMember(target, negated, property) });
|
|
151
|
-
const guardedMember = (target, negated, property) => property === "not" ? guardOf(target.not, !negated) : forwardedMember(target, negated, property);
|
|
152
|
-
const forwardedMember = (target, negated, property) => refusedMember(property, negated) ?? memberAt(target, property);
|
|
153
|
-
const plainAssertion = (actual, message) => counted(guardOf(V.expect(actual, message), false));
|
|
154
|
-
const countsInThisRun = () => lookupRun()?.shadow !== true;
|
|
155
|
-
/**
|
|
156
|
-
* A check outside a soft run counts on the run it happens in: an `owned` region's checks throw, and a
|
|
157
|
-
* check outside every run has no gate to satisfy.
|
|
158
|
-
*/
|
|
159
|
-
const counted = (assertion) => {
|
|
160
|
-
if (countsInThisRun()) recordAssertion$1();
|
|
161
|
-
return assertion;
|
|
162
|
-
};
|
|
163
|
-
const softAssertion = (running, actual, message) => guardOf(running.ctx.expect.soft(actual, message), false);
|
|
164
|
-
const isSoftRun = (running) => running.shadow === false && isOwned() === false;
|
|
165
|
-
const liveAssertion = (running, actual, message) => isSoftRun(running) ? softAssertion(running, actual, message) : plainAssertion(actual, message);
|
|
166
|
-
/**
|
|
167
|
-
* A check inside a run is soft: it records the failure and the test stops at the next step. The second run
|
|
168
|
-
* and an `owned` region throw, so their own handler sees the failure.
|
|
169
|
-
*/
|
|
170
|
-
const assertionFor = (actual, message) => {
|
|
171
|
-
const running = lookupRun();
|
|
172
|
-
return running === void 0 ? plainAssertion(actual, message) : liveAssertion(running, actual, message);
|
|
173
|
-
};
|
|
174
|
-
Function.dual(2, assertionFor);
|
|
175
|
-
const messageOf = (value) => typeof value === "string" ? value : void 0;
|
|
176
|
-
const applyCheck = (args) => assertionFor(args[0], messageOf(args[1]));
|
|
177
|
-
/** The statics are Vitest's; only `soft` is the fork's, and soft is what a check already is inside a run. */
|
|
178
|
-
const makeExpect = () => new Proxy(V.expect, {
|
|
179
|
-
get: (target, property) => property === "soft" ? assertionFor : memberAt(target, property),
|
|
180
|
-
apply: (_target, _thisArg, args) => applyCheck(args)
|
|
181
|
-
});
|
|
182
|
-
/**
|
|
183
|
-
* The fork's `expect`: soft within one Effect step, refusing the slop forms.
|
|
184
|
-
*
|
|
185
|
-
* @internal
|
|
186
|
-
*/
|
|
187
|
-
const expect$1 = makeExpect();
|
|
188
|
-
const refuseHookNow = () => refuse(refuseHook);
|
|
189
|
-
const takesHook = (args) => typeof args[0] === "function";
|
|
190
|
-
const refuseHookPair = (fn, timeout) => {
|
|
191
|
-
return refuseHookNow();
|
|
192
|
-
};
|
|
193
|
-
/**
|
|
194
|
-
* @internal
|
|
195
|
-
*/
|
|
196
|
-
const beforeEach$1 = Function.dual(takesHook, refuseHookPair);
|
|
197
|
-
/**
|
|
198
|
-
* @internal
|
|
199
|
-
*/
|
|
200
|
-
const afterEach$1 = Function.dual(takesHook, refuseHookPair);
|
|
201
|
-
/**
|
|
202
|
-
* @internal
|
|
203
|
-
*/
|
|
204
|
-
const beforeAll$1 = Function.dual(takesHook, V.beforeAll);
|
|
205
|
-
/**
|
|
206
|
-
* @internal
|
|
207
|
-
*/
|
|
208
|
-
const afterAll$1 = Function.dual(takesHook, V.afterAll);
|
|
209
|
-
//#endregion
|
|
210
|
-
//#region src/internal/property/defaults.ts
|
|
211
|
-
/** @internal The `test.provide` key the shared config fills with this run's tier's check options. */
|
|
212
|
-
const checkDefaultsKey = "@systemfsoftware/vitest:property-check";
|
|
213
|
-
/** @internal The configured property-check defaults, or `undefined` when the run provided none. */
|
|
214
|
-
const providedCheckDefaults = () => inject(checkDefaultsKey);
|
|
215
|
-
//#endregion
|
|
216
133
|
//#region src/internal/property/coverage.ts
|
|
217
134
|
/**
|
|
218
135
|
* @internal The R14 coverage classes: labelled input predicates, each with a minimum share of runs.
|
|
@@ -678,42 +595,6 @@ const makeProperty = (runtime) => {
|
|
|
678
595
|
};
|
|
679
596
|
};
|
|
680
597
|
//#endregion
|
|
681
|
-
//#region src/internal/step-boundary.ts
|
|
682
|
-
/** @internal */
|
|
683
|
-
const makeStopState = (failed) => ({
|
|
684
|
-
stopped: /* @__PURE__ */ new WeakSet(),
|
|
685
|
-
failed
|
|
686
|
-
});
|
|
687
|
-
const alreadyStopped = (state, fiber) => state.stopped.has(fiber);
|
|
688
|
-
/** @internal */
|
|
689
|
-
const stopChecker = Function.dual(2, (state, fiber) => alreadyStopped(state, fiber) === false && state.failed());
|
|
690
|
-
const markStopped = (state, fiber) => {
|
|
691
|
-
state.stopped.add(fiber);
|
|
692
|
-
};
|
|
693
|
-
/** @internal */
|
|
694
|
-
const stopAfterFailedCheck = Function.dual(2, (scheduler, state) => ({
|
|
695
|
-
executionMode: scheduler.executionMode,
|
|
696
|
-
makeDispatcher: () => scheduler.makeDispatcher(),
|
|
697
|
-
shouldYield: (fiber) => checkFiber(state, scheduler, fiber)
|
|
698
|
-
}));
|
|
699
|
-
const checkFiber = (state, scheduler, fiber) => {
|
|
700
|
-
const steppable = fiber;
|
|
701
|
-
if (stopChecker(state, steppable) === false) return scheduler.shouldYield(fiber);
|
|
702
|
-
markStopped(state, steppable);
|
|
703
|
-
steppable.interruptUnsafe(steppable.id);
|
|
704
|
-
return true;
|
|
705
|
-
};
|
|
706
|
-
//#endregion
|
|
707
|
-
//#region src/internal/test-context.ts
|
|
708
|
-
/** @internal */
|
|
709
|
-
const vitestTestContextKey$1 = "@systemfsoftware/vitest/TestContext";
|
|
710
|
-
/**
|
|
711
|
-
* The running Vitest test, or `null` outside a run; provided on every test the fork runs.
|
|
712
|
-
*
|
|
713
|
-
* @internal
|
|
714
|
-
*/
|
|
715
|
-
const VitestTestContext$1 = Context.Reference(vitestTestContextKey$1, { defaultValue: () => null });
|
|
716
|
-
//#endregion
|
|
717
598
|
//#region src/internal/virtual-time.ts
|
|
718
599
|
const roundNanos = (millis) => BigInt(Math.round(millis * 1e6));
|
|
719
600
|
const byWakeOrder = (first, second) => {
|
|
@@ -885,6 +766,18 @@ const virtualClockOf = (runtime) => {
|
|
|
885
766
|
};
|
|
886
767
|
//#endregion
|
|
887
768
|
//#region src/internal/runner.ts
|
|
769
|
+
/**
|
|
770
|
+
* The fork's runner: the test lanes, the layer blocks, the property lanes and the run pipeline.
|
|
771
|
+
*
|
|
772
|
+
* A test body is a generator the driver steps (KTD1), so `expect` is the body's own parameter and every check is
|
|
773
|
+
* yielded. Everything #512 promised holds unchanged: a fresh build of a test's layers, virtual time that advances
|
|
774
|
+
* when the fibers are idle, concurrent and shuffled blocks, a declared-shared block as the only sharing, a second
|
|
775
|
+
* run on a fresh build that reports leaked state, and Effect `Equal` in `toEqual`.
|
|
776
|
+
*
|
|
777
|
+
* What is a refusal here: a body that is not a generator, and the habit lanes `it.effect`, `it.scoped` and
|
|
778
|
+
* `it.scopedLive` (R2, R9). Registration marks every task this file adds (KTD8), so the guard in
|
|
779
|
+
* `@systemfsoftware/vitest/guard` can tell the fork's tests from a test registered by Vitest itself.
|
|
780
|
+
*/
|
|
888
781
|
const getCurrentSuite = V.TestRunner.getCurrentSuite;
|
|
889
782
|
/** @internal */
|
|
890
783
|
const addEqualityTesters$1 = () => {
|
|
@@ -896,80 +789,66 @@ const givenOptions = (timeout) => timeout ?? {};
|
|
|
896
789
|
const testOptions = (timeout) => typeof timeout === "number" ? numbered(timeout) : givenOptions(timeout);
|
|
897
790
|
/** @internal */
|
|
898
791
|
const hookTimeout = (timeout) => timeout === void 0 ? void 0 : Duration.toMillis(Duration.fromInputUnsafe(timeout));
|
|
792
|
+
const isName = (first) => typeof first === "string";
|
|
793
|
+
const byName = (args) => isName(args[0]);
|
|
794
|
+
const byLayer = (args) => Layer.isLayer(args[0]);
|
|
795
|
+
const byEffect = (args) => Effect.isEffect(args[0]);
|
|
899
796
|
/**
|
|
900
|
-
* The positional form is refused at run time with the R15 rewrite; the public type
|
|
797
|
+
* The positional property form is refused at run time with the R15 rewrite; the public type refuses it first
|
|
798
|
+
* because a `PropertySpec` is an object and the positional shape is not.
|
|
901
799
|
*/
|
|
800
|
+
const refusePositionalProp = "✗ it.prop(name, [arbitraries], predicate) is removed. Name the function under test and pass a budget: it.prop(name, { of, subject, runs }, holds).";
|
|
801
|
+
const isAbsent = (spec) => spec === null || spec === void 0;
|
|
802
|
+
const hasNoOf = (spec) => Object.hasOwn(spec, "of") === false;
|
|
803
|
+
const lacksInputs = (spec) => isAbsent(spec) || hasNoOf(spec);
|
|
902
804
|
const refusePositional = (spec) => {
|
|
903
805
|
if (lacksInputs(spec)) throw new Slop({ detail: refusePositionalProp });
|
|
904
806
|
};
|
|
905
|
-
const lacksInputs = (spec) => isAbsent(spec) || hasNoOf(spec);
|
|
906
|
-
const isAbsent = (spec) => spec === null || spec === void 0;
|
|
907
|
-
const hasNoOf = (spec) => Object.hasOwn(spec, "of") === false;
|
|
908
|
-
const stopCheckOf = (ctx) => ctx === void 0 ? void 0 : failedAfter(ctx, errorCount(ctx));
|
|
909
|
-
const neverStop = () => void 0;
|
|
910
807
|
/** @internal */
|
|
911
808
|
const realTime = {
|
|
912
809
|
runtime: void 0,
|
|
913
|
-
owned: false
|
|
914
|
-
stop: stopCheckOf
|
|
810
|
+
owned: false
|
|
915
811
|
};
|
|
916
|
-
/**
|
|
812
|
+
/**
|
|
813
|
+
* A run with its own virtual clock: the clock only ever advances when the run's fibers are idle.
|
|
814
|
+
*
|
|
815
|
+
* @internal
|
|
816
|
+
*/
|
|
917
817
|
const perRun = () => ({
|
|
918
818
|
runtime: makeVirtualRuntime(),
|
|
919
|
-
owned: true
|
|
920
|
-
stop: stopCheckOf
|
|
819
|
+
owned: true
|
|
921
820
|
});
|
|
922
|
-
const propertyEnv =
|
|
923
|
-
runtime: void 0,
|
|
924
|
-
owned: false,
|
|
925
|
-
stop: neverStop
|
|
926
|
-
};
|
|
821
|
+
const propertyEnv = realTime;
|
|
927
822
|
const logErrors = (cause) => Effect.forEach(Cause.prettyErrors(cause), (error) => Effect.logError(error), { discard: true });
|
|
928
|
-
const failedAfter = (ctx, before) => () => errorCount(ctx) > before;
|
|
929
823
|
const signalOf = (ctx) => ctx?.signal;
|
|
930
824
|
const signalOnly = (signal) => signal === void 0 ? {} : { signal };
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
825
|
+
const schedulerOf = (env) => env.runtime === void 0 ? {} : { scheduler: env.runtime.scheduler };
|
|
826
|
+
const runOptions = (ctx, env) => ({
|
|
827
|
+
...signalOnly(signalOf(ctx)),
|
|
828
|
+
...schedulerOf(env)
|
|
934
829
|
});
|
|
935
|
-
const runOptions = (ctx, scheduler) => scheduler === void 0 ? signalOnly(signalOf(ctx)) : withScheduler(signalOf(ctx), scheduler);
|
|
936
|
-
const baseScheduler = (env) => env.runtime?.scheduler;
|
|
937
|
-
const stopScheduler = (base, failedCheck) => stopAfterFailedCheck(base ?? new Scheduler.MixedScheduler(), makeStopState(failedCheck));
|
|
938
|
-
const pickScheduler = (env, failedCheck) => failedCheck === void 0 ? baseScheduler(env) : stopScheduler(baseScheduler(env), failedCheck);
|
|
939
|
-
const runPromise = (effect, ctx, env = realTime) => {
|
|
940
|
-
const failedCheck = env.stop(ctx);
|
|
941
|
-
return runExit(effect, ctx, env, failedCheck).then(reportExit(failedCheck));
|
|
942
|
-
};
|
|
943
|
-
const runExit = (effect, ctx, env, failedCheck) => {
|
|
944
|
-
const program = wrapOwned(effect, ownedRuntime(env));
|
|
945
|
-
return Effect.runPromiseExit(program, runOptions(ctx, pickScheduler(env, failedCheck)));
|
|
946
|
-
};
|
|
947
830
|
const ownedRuntime = (env) => env.owned === false ? void 0 : env.runtime;
|
|
948
831
|
const observeOf = (runtime) => runtime === void 0 ? Effect.void : runtime.observe;
|
|
949
832
|
const endOf = (runtime) => runtime === void 0 ? Effect.void : runtime.end;
|
|
950
833
|
const wrapOwned = (effect, runtime) => observeOf(runtime).pipe(Effect.andThen(effect.pipe(Effect.ensuring(endOf(runtime)))));
|
|
951
|
-
const
|
|
952
|
-
const stoppedExit = (cause) => Cause.hasInterruptsOnly(cause) ? stoppedSettled() : failExit(cause);
|
|
953
|
-
const stoppedChecked = (cause, failed) => failed ? stoppedExit(cause) : failExit(cause);
|
|
954
|
-
const failedOrThrow = (cause, failedCheck) => failedCheck === void 0 ? failExit(cause) : stoppedChecked(cause, failedCheck());
|
|
955
|
-
const reportExit = (failedCheck) => (exit) => Exit.isSuccess(exit) ? Promise.resolve(exit.value) : failedOrThrow(exit.cause, failedCheck);
|
|
834
|
+
const runExit = (effect, ctx, env) => Effect.runPromiseExit(wrapOwned(effect, ownedRuntime(env)), runOptions(ctx, env));
|
|
956
835
|
/**
|
|
957
836
|
* A body that needs a service nothing provides dies with Effect's own `Service not found`. That is a
|
|
958
|
-
* test-authoring error — the layers were never wired — so it is reported as the unprovided rewrite (R9)
|
|
959
|
-
*
|
|
960
|
-
* and never reach here.
|
|
837
|
+
* test-authoring error — the layers were never wired — so it is reported as the unprovided rewrite (R9) instead
|
|
838
|
+
* of as a raw defect.
|
|
961
839
|
*/
|
|
962
840
|
const isUnprovided = (error) => error instanceof Error && error.message.startsWith("Service not found");
|
|
963
|
-
const failExit = (cause) => logErrors(cause).pipe(Effect.runPromise).then(() => rethrowSquashed(cause));
|
|
964
841
|
const rethrowSquashed = (cause) => {
|
|
965
842
|
const error = Cause.squash(cause);
|
|
966
|
-
if (isUnprovided(error)) throw new Slop({ detail:
|
|
843
|
+
if (isUnprovided(error)) throw new Slop({ detail: unprovidedText });
|
|
967
844
|
throw error;
|
|
968
845
|
};
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
846
|
+
const failExit = (cause) => logErrors(cause).pipe(Effect.runPromise).then(() => rethrowSquashed(cause));
|
|
847
|
+
const reportExit = (exit) => Exit.isSuccess(exit) ? Promise.resolve(exit.value) : failExit(exit.cause);
|
|
848
|
+
const runPromise = (effect, ctx, env = realTime) => runExit(effect, ctx, env).then(reportExit);
|
|
849
|
+
const noop = () => {};
|
|
850
|
+
const detach = (ctx, onAbort) => {
|
|
851
|
+
ctx.signal.removeEventListener("abort", onAbort);
|
|
973
852
|
};
|
|
974
853
|
const trackAbort = (ctx, promise) => {
|
|
975
854
|
const onAbort = () => {
|
|
@@ -979,168 +858,104 @@ const trackAbort = (ctx, promise) => {
|
|
|
979
858
|
promise.then(() => detach(ctx, onAbort), () => detach(ctx, onAbort));
|
|
980
859
|
if (ctx.signal.aborted) onAbort();
|
|
981
860
|
};
|
|
982
|
-
const
|
|
983
|
-
|
|
861
|
+
const runTest = (ctx, env = realTime) => (effect) => {
|
|
862
|
+
const promise = runPromise(effect, ctx, env);
|
|
863
|
+
if (ctx !== void 0) trackAbort(ctx, promise);
|
|
864
|
+
return promise;
|
|
984
865
|
};
|
|
985
|
-
const noop = () => {};
|
|
986
866
|
const clockLayerOf = (env) => env.runtime === void 0 ? TestClock.layer() : virtualClockLayer(env.runtime);
|
|
987
867
|
/** @internal */
|
|
988
868
|
const testEnv = (env) => Layer.mergeAll(TestConsole.layer, clockLayerOf(env));
|
|
989
869
|
/**
|
|
990
|
-
* The run a test's fibers execute in: the test's own context
|
|
991
|
-
*
|
|
870
|
+
* The run a test's fibers execute in: the test's own context under the fork's `VitestTestContext`, so a library
|
|
871
|
+
* built on the fork reads the same context the runner does.
|
|
992
872
|
*/
|
|
993
|
-
const bindRun = (effect, ctx
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
};
|
|
1010
|
-
const outputIsEffect = (output) => Effect.isEffect(output);
|
|
1011
|
-
const buildSingleRun = (body, mapOne, env, shadow) => (ctx) => {
|
|
1012
|
-
const output = withSyncRun({
|
|
1013
|
-
ctx,
|
|
1014
|
-
shadow
|
|
1015
|
-
}, () => body(ctx));
|
|
1016
|
-
checkPromiseBody(output);
|
|
1017
|
-
return outputIsEffect(output) ? runBuilt(ctx, env, mapOne, output, shadow) : Promise.resolve(void 0);
|
|
1018
|
-
};
|
|
1019
|
-
const runBuilt = (ctx, env, mapOne, output, shadow) => runTest(ctx, env)(mapOne(bindRun(Effect.suspend(() => output), ctx, shadow)));
|
|
873
|
+
const bindRun = (effect, ctx) => effect.pipe(Effect.provideService(VitestTestContext$1, ctx));
|
|
874
|
+
const refuseBody = (text) => {
|
|
875
|
+
throw refusalOf$1(text);
|
|
876
|
+
};
|
|
877
|
+
/** The constructor names of the function kinds that hand back a promise instead of a generator. */
|
|
878
|
+
const AWAITED_KINDS = {
|
|
879
|
+
AsyncFunction: true,
|
|
880
|
+
AsyncGeneratorFunction: true
|
|
881
|
+
};
|
|
882
|
+
/** The constructor name of the one function kind the driver steps: a generator hands its steps over. */
|
|
883
|
+
const GENERATOR_KIND = "GeneratorFunction";
|
|
884
|
+
const kindOf = (body) => body.constructor.name;
|
|
885
|
+
const isGeneratorBody = (body) => kindOf(body) === GENERATOR_KIND;
|
|
886
|
+
const isSteps = (returned) => "next" in returned && typeof returned.next === "function";
|
|
887
|
+
/** The iterator a generator handed over; only a generator reaches here, so the guard is for the compiler alone. */
|
|
888
|
+
const asSteps = (returned) => isSteps(returned) ? returned : refuseBody(refuseSyncBody);
|
|
1020
889
|
/**
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
890
|
+
* The refusal a body reaches before it runs (R2). An `async` body hands back a promise and runs outside the test
|
|
891
|
+
* runtime. A plain body can only be told from one by calling it, and nothing in a refused body may run, so it is
|
|
892
|
+
* refused as a sync body, whose rewrite is to pass a generator; the compile channel prints each shape's own text.
|
|
1024
893
|
*/
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
894
|
+
const refusalOf = (body) => AWAITED_KINDS[kindOf(body)] === true ? refuseAsyncBody : refuseSyncBody;
|
|
895
|
+
/** The body's generator, or the refusal for a body the driver cannot step. */
|
|
896
|
+
const stepsOfBody = (body, checks) => isGeneratorBody(body) ? asSteps(body(checks)) : refuseBody(refusalOf(body));
|
|
897
|
+
/** The same for a row body: Vitest's `it.for` order is the row first, then the checks (R9). */
|
|
898
|
+
const stepsOfRow = (row, body, checks) => isGeneratorBody(body) ? asSteps(body(row, checks)) : refuseBody(refusalOf(body));
|
|
899
|
+
const bodyOf = (ledger, body) => () => asSteps(body(checksFor(ledger)));
|
|
900
|
+
/** The body a lane registered, closed over its checks: the generator itself, or the refusal for what it is not. */
|
|
901
|
+
const laneBodyOf = (body) => (checks) => typeof body === "string" ? refuseBody(refuseSyncBody) : stepsOfBody(body, checks);
|
|
902
|
+
/** The row body a lane registered: Vitest's `it.for` order is the row first, then the checks (R9). */
|
|
903
|
+
const rowBodyOf = (row, body) => (checks) => typeof body === "string" ? refuseBody(refuseSyncBody) : stepsOfRow(row, body, checks);
|
|
904
|
+
/** One test's run: a fresh ledger, and the lane's own services around the program the driver built. */
|
|
905
|
+
const runLaned = (ctx, mapEffect, env, body) => {
|
|
906
|
+
const ledger = makeLedger(ctx);
|
|
907
|
+
return runTest(ctx, env)(mapEffect(bindRun(drive(bodyOf(ledger, body), [], ledger), ctx), env));
|
|
1039
908
|
};
|
|
1040
|
-
/** @internal */
|
|
1041
|
-
const prop$1 = Function.dual(byName, propImpl);
|
|
1042
909
|
const outcomeOf = (promise) => promise.then(() => void 0, (error) => error);
|
|
1043
|
-
/**
|
|
1044
|
-
* The second run's failure: a diagnosis the fork itself made is rethrown as that diagnosis, because the
|
|
1045
|
-
* second run exists to catch leaked state, not to relabel a refusal as a leak (F7). Anything else is the leak.
|
|
1046
|
-
*/
|
|
1047
|
-
const throwSecondRun = (error) => {
|
|
1048
|
-
if (isRefusal(error)) throw error;
|
|
1049
|
-
return throwLeaked(error);
|
|
1050
|
-
};
|
|
1051
|
-
const leakedOrNothing = (error) => {
|
|
1052
|
-
if (error !== void 0) throwSecondRun(error);
|
|
1053
|
-
};
|
|
1054
|
-
const makeTesterWith = (mapEffect, it, envFor, rerunnable) => {
|
|
1055
|
-
const runOne = (ctx, args, self, shadow) => {
|
|
1056
|
-
const env = envFor();
|
|
1057
|
-
return buildSingleRun(() => self(...args), (effect) => mapEffect(effect, env), env, shadow)(ctx);
|
|
1058
|
-
};
|
|
1059
|
-
/**
|
|
1060
|
-
* The second run: the same layer build the first run used, built again, with the shadow flag set so its
|
|
1061
|
-
* checks throw instead of reporting softly (R5, KTD7).
|
|
1062
|
-
*/
|
|
1063
|
-
const rerun = (ctx, args, self) => outcomeOf(runOne(ctx, args, self, true)).then(leakedOrNothing);
|
|
1064
|
-
const run = (ctx, args, self) => outcomeOf(runOne(ctx, args, self, false)).then((error) => afterFirst(ctx, args, self, error));
|
|
1065
|
-
const afterFirst = (ctx, args, self, error) => {
|
|
1066
|
-
if (error !== void 0) throwAfterCheck(ctx, error);
|
|
1067
|
-
gateNoAssertion(ctx);
|
|
1068
|
-
return rerunIfClean(ctx, args, self);
|
|
1069
|
-
};
|
|
1070
|
-
const rerunIfClean = (ctx, args, self) => shouldRerun(ctx) ? rerun(ctx, args, self) : Promise.resolve();
|
|
1071
|
-
const shouldRerun = (ctx) => rerunnable && isClean(ctx);
|
|
1072
|
-
const registerOn = (registrar, name, self, timeout) => {
|
|
1073
|
-
registrar(name, testOptions(timeout), (ctx) => run(ctx, [ctx], self));
|
|
1074
|
-
};
|
|
1075
|
-
const dualTest = (register) => Function.dual(byName, register);
|
|
1076
|
-
const f = dualTest((name, self, timeout) => registerOn(it, name, self, timeout));
|
|
1077
|
-
const skip = dualTest((name, self, timeout) => registerOn(it.skip, name, self, timeout));
|
|
1078
|
-
const skipIf = (condition) => dualTest((name, self, timeout) => registerOn(it.skipIf(condition), name, self, timeout));
|
|
1079
|
-
const runIf = (condition) => dualTest((name, self, timeout) => registerOn(it.runIf(condition), name, self, timeout));
|
|
1080
|
-
const only = dualTest((name, self, timeout) => registerOn(it.only, name, self, timeout));
|
|
1081
|
-
const each = (cases) => (name, self, timeout) => it.for(cases)(name, testOptions(timeout), (row, ctx) => run(ctx, [row], self));
|
|
1082
|
-
const fails = dualTest((name, self, timeout) => registerOn(V.it.fails, name, self, timeout));
|
|
1083
|
-
const effectProperty = makeProperty({
|
|
1084
|
-
register: (name, program) => {
|
|
1085
|
-
it(name, (ctx) => runProperty(ctx, program));
|
|
1086
|
-
},
|
|
1087
|
-
provide: (effect) => mapEffect(effect, envFor())
|
|
1088
|
-
});
|
|
1089
|
-
const testerProp = (name, spec, holds) => {
|
|
1090
|
-
refusePositional(spec);
|
|
1091
|
-
effectProperty.effectProp(name, spec, holds);
|
|
1092
|
-
};
|
|
1093
|
-
return Object.assign(f, {
|
|
1094
|
-
skip,
|
|
1095
|
-
skipIf,
|
|
1096
|
-
runIf,
|
|
1097
|
-
only,
|
|
1098
|
-
each,
|
|
1099
|
-
fails,
|
|
1100
|
-
prop: testerProp
|
|
1101
|
-
});
|
|
1102
|
-
};
|
|
1103
|
-
const makeTester = (mapEffect, it = V.it) => makeTesterWith(mapEffect, it, perRun, true);
|
|
1104
|
-
const makeSharedTester = (mapEffect, it, envFor) => makeTesterWith(mapEffect, it, envFor, false);
|
|
1105
910
|
const isSlop = (error) => Schema$1.is(Slop)(error);
|
|
1106
|
-
/** Every failure the fork itself names
|
|
1107
|
-
const
|
|
911
|
+
/** Every failure the fork itself names: the branded refusals, plus the Schema-tagged families. */
|
|
912
|
+
const otherRefusals = [
|
|
1108
913
|
isSlop,
|
|
1109
|
-
Schema$1.is(AfterFailedExpect),
|
|
1110
914
|
Schema$1.is(InvalidBudget),
|
|
1111
915
|
Schema$1.is(NonBooleanVerdict),
|
|
1112
916
|
Schema$1.is(LeakedState)
|
|
1113
917
|
];
|
|
1114
|
-
const isRefusal = (error) =>
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
};
|
|
1119
|
-
const afterFailedCheck = (ctx, error) => {
|
|
1120
|
-
if (errorCount(ctx) > 0) throwAfterFailed();
|
|
1121
|
-
throw error;
|
|
1122
|
-
};
|
|
1123
|
-
const throwAfterFailed = () => {
|
|
1124
|
-
throw new AfterFailedExpect({ detail: "thrown after a failed expect above, most likely caused by it" });
|
|
918
|
+
const isRefusal = (error) => isRefusal$1(error) || otherRefusals.some((matches) => matches(error));
|
|
919
|
+
const clipped = (message) => message.length > 400 ? message.slice(0, 400) : message;
|
|
920
|
+
const throwLeaked = (outcome) => {
|
|
921
|
+
throw new LeakedState({ detail: "✗ this test passed, then failed when run again on a fresh build of its services: something keeps state outside its layer, so tests here see each other's changes. The second run failed with: " + clipped(outcome.message) });
|
|
1125
922
|
};
|
|
1126
|
-
/** Whether a run left no failure behind: the second run is only worth doing for a clean first one. */
|
|
1127
|
-
const isClean = (ctx) => errorCount(ctx) === 0;
|
|
1128
|
-
const silentRun = (ctx) => errorCount(ctx) === 0 && assertionCalls(ctx) === 0;
|
|
1129
923
|
/**
|
|
1130
|
-
* The
|
|
1131
|
-
*
|
|
924
|
+
* The second run's failure: a diagnosis the fork itself made is rethrown as that diagnosis, because the second
|
|
925
|
+
* run exists to catch leaked state, not to relabel a refusal as a leak. Anything else is the leak.
|
|
1132
926
|
*/
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
927
|
+
const throwSecondRun = (error) => {
|
|
928
|
+
if (isRefusal(error)) throw error;
|
|
929
|
+
return throwLeaked(error);
|
|
1136
930
|
};
|
|
1137
|
-
const
|
|
1138
|
-
|
|
931
|
+
const leakedOrNothing = (error) => {
|
|
932
|
+
if (error !== void 0) throwSecondRun(error);
|
|
1139
933
|
};
|
|
1140
|
-
const
|
|
1141
|
-
const
|
|
1142
|
-
|
|
934
|
+
const NO_ERRORS = [];
|
|
935
|
+
const presentErrors = (errors) => errors === void 0 ? NO_ERRORS : errors;
|
|
936
|
+
/** Whether a run left no failure behind: only a clean first run is worth running a second time. */
|
|
937
|
+
const isClean = (ctx) => presentErrors(ctx.task.result?.errors).length === 0;
|
|
938
|
+
/**
|
|
939
|
+
* Runs a property's program as the test that registered it, under the same context the generator lanes use. The
|
|
940
|
+
* env is the bare `propertyEnv`: a `false` verdict is the property's own shrink path, so nothing interrupts it.
|
|
941
|
+
*/
|
|
942
|
+
const runProperty = (ctx, program) => runTest(ctx, propertyEnv)(bindRun(Effect.suspend(program), ctx));
|
|
943
|
+
const property = makeProperty({
|
|
944
|
+
register: (name, program) => marked(() => V.it(name, (ctx) => runProperty(ctx, program))),
|
|
945
|
+
provide: (effect) => effect
|
|
946
|
+
});
|
|
947
|
+
const propImpl = (name, spec, holds) => {
|
|
948
|
+
refusePositional(spec);
|
|
949
|
+
property.prop(name, spec, holds);
|
|
1143
950
|
};
|
|
951
|
+
/** @internal */
|
|
952
|
+
const prop$1 = Function.dual(byName, propImpl);
|
|
953
|
+
/**
|
|
954
|
+
* The law kinds over the sync property family: the top-level `it.law`.
|
|
955
|
+
*
|
|
956
|
+
* @internal
|
|
957
|
+
*/
|
|
958
|
+
const law$1 = property.law;
|
|
1144
959
|
const CONCURRENT_BLOCK = {
|
|
1145
960
|
concurrent: true,
|
|
1146
961
|
shuffle: true
|
|
@@ -1158,25 +973,24 @@ const memoMapIn = (options) => options?.memoMap;
|
|
|
1158
973
|
const excludeTestServicesOf = (options) => withDefault(excludedIn(options), false);
|
|
1159
974
|
const blockEnvOf = (inherited) => withDefault(inherited, {
|
|
1160
975
|
runtime: makeVirtualRuntime(),
|
|
1161
|
-
owned: false
|
|
1162
|
-
stop: stopCheckOf
|
|
976
|
+
owned: false
|
|
1163
977
|
});
|
|
1164
978
|
const memoMapOf = (options) => withDefault(memoMapIn(options), Effect.runSync(Layer.makeMemoMap));
|
|
1165
979
|
const blockHookTimeout = (options) => hookTimeout(timeoutIn(options));
|
|
1166
980
|
const isSharedBlock = (options) => sharedIn(options) === true;
|
|
1167
|
-
/**
|
|
1168
|
-
* A named block's suite options: its tests run concurrently and in shuffled order (R4). A declared-shared
|
|
1169
|
-
* block is the stateful exception — its tests read each other's writes through one build, so it stays in
|
|
1170
|
-
* declaration order and runs alone.
|
|
1171
|
-
*/
|
|
1172
|
-
const describeOptions = (options, shared) => mergeBlockOptions(options, shared ? ALONE_BLOCK : CONCURRENT_BLOCK);
|
|
1173
981
|
const mergeBlockOptions = (options, fallback) => ({
|
|
1174
982
|
concurrent: withDefault(concurrencyIn(options), fallback.concurrent),
|
|
1175
983
|
shuffle: fallback.shuffle
|
|
1176
984
|
});
|
|
1177
985
|
/**
|
|
1178
|
-
*
|
|
1179
|
-
* is
|
|
986
|
+
* A named block's suite options: its tests run concurrently and in shuffled order (R4). A declared-shared block
|
|
987
|
+
* is the stateful exception — its tests read each other's writes through one build, so it stays in declaration
|
|
988
|
+
* order and runs alone.
|
|
989
|
+
*/
|
|
990
|
+
const describeOptions = (options, shared) => mergeBlockOptions(options, shared ? ALONE_BLOCK : CONCURRENT_BLOCK);
|
|
991
|
+
/**
|
|
992
|
+
* The block's layer with the test's own services merged in, unless the block excludes them. `Layer`'s output is
|
|
993
|
+
* contravariant, so the merged layer is the *narrower* `Layer<R, E>` the callers provide to a run.
|
|
1180
994
|
*/
|
|
1181
995
|
const mergeTestEnv = (layer_, env, excludeTestServices) => excludeTestServices ? layer_ : Layer.provideMerge(layer_, env);
|
|
1182
996
|
const sharedContext = (shared, memoMap, scope) => Layer.buildWithMemoMap(shared, memoMap, scope).pipe(Effect.orDie, Effect.cached, Effect.runSync);
|
|
@@ -1200,6 +1014,31 @@ const routeBlock = (first, second, anonymous, named) => {
|
|
|
1200
1014
|
const openNamed = (name, f, named) => {
|
|
1201
1015
|
if (f !== void 0) named(name, f);
|
|
1202
1016
|
};
|
|
1017
|
+
/** Runs a registration and marks every test task it added, which is how the guard knows the fork registered it. */
|
|
1018
|
+
const marked = (register) => {
|
|
1019
|
+
const before = suiteTasks(getCurrentSuite()).length;
|
|
1020
|
+
register();
|
|
1021
|
+
markAdded(before);
|
|
1022
|
+
};
|
|
1023
|
+
const markAdded = (before) => {
|
|
1024
|
+
for (const task of collectTasks(suiteTasks(getCurrentSuite()).slice(before))) markTask(task);
|
|
1025
|
+
};
|
|
1026
|
+
const blockTasksOf = () => suiteTasks(getCurrentSuite());
|
|
1027
|
+
const suiteTasks = (suite) => suite.tasks;
|
|
1028
|
+
const collectTasks = (tasks) => {
|
|
1029
|
+
const acc = [];
|
|
1030
|
+
for (const task of tasks) collectOne(task, acc);
|
|
1031
|
+
return acc;
|
|
1032
|
+
};
|
|
1033
|
+
const collectNested = (task, acc) => {
|
|
1034
|
+
if (task.tasks !== void 0) collectTasks(task.tasks).forEach((nested) => acc.push(nested));
|
|
1035
|
+
};
|
|
1036
|
+
const collectOne = (task, acc) => {
|
|
1037
|
+
if (isRunnableTest(task)) acc.push(task);
|
|
1038
|
+
else collectNested(task, acc);
|
|
1039
|
+
};
|
|
1040
|
+
const notSkipped = (task) => task.mode !== "skip" && task.mode !== "todo";
|
|
1041
|
+
const isRunnableTest = (task) => task.type === "test" && notSkipped(task);
|
|
1203
1042
|
const layerFor = (layer_, options, inherited) => isSharedBlock(options) ? sharedBlock(layer_, options, inherited) : freshBlock(layer_, options);
|
|
1204
1043
|
const freshBlock = (layer_, options) => {
|
|
1205
1044
|
const excludeTestServices = excludeTestServicesOf(options);
|
|
@@ -1209,13 +1048,7 @@ const freshBlock = (layer_, options) => {
|
|
|
1209
1048
|
excludeTestServices
|
|
1210
1049
|
}, void 0);
|
|
1211
1050
|
const nestedLayer = Function.dual(byLayer, nestedOpen);
|
|
1212
|
-
const makeIt = (it) =>
|
|
1213
|
-
effect: makeTester((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, Layer.orDie(Layer.fresh(withTestEnv(env))))), it),
|
|
1214
|
-
prop: prop$1,
|
|
1215
|
-
law: property.law,
|
|
1216
|
-
flakyTest: flakyTest$1,
|
|
1217
|
-
layer: nestedLayer
|
|
1218
|
-
}, true);
|
|
1051
|
+
const makeIt = (it) => methodsFor((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, Layer.orDie(Layer.fresh(withTestEnv(env))))), it, perRun, true, nestedLayer);
|
|
1219
1052
|
return blockOpener((f) => f(makeIt(V.it)), (name, f) => {
|
|
1220
1053
|
V.describe(name, describeOptions(options, false), () => f(makeIt(V.it)));
|
|
1221
1054
|
});
|
|
@@ -1225,24 +1058,15 @@ const nestedBlock = (nested, memoMap) => ({
|
|
|
1225
1058
|
memoMap: Layer.forkMemoMapUnsafe(memoMap),
|
|
1226
1059
|
shared: true
|
|
1227
1060
|
});
|
|
1228
|
-
const sharedOverrides = (shared, contextEffect, memoMap, blockEnv) => {
|
|
1229
|
-
const nestedOpen = (nestedLayer, nestedOptions) => layerFor(() => Layer.provideMerge(nestedLayer, shared), nestedBlock(nestedOptions, memoMap), blockEnv);
|
|
1230
|
-
const nestedLayer = Function.dual(byLayer, nestedOpen);
|
|
1231
|
-
return {
|
|
1232
|
-
effect: makeSharedTester((effect) => Effect.flatMap(contextEffect, (context) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, context))), V.it, () => blockEnv),
|
|
1233
|
-
prop: prop$1,
|
|
1234
|
-
law: property.law,
|
|
1235
|
-
flakyTest: flakyTest$1,
|
|
1236
|
-
layer: nestedLayer
|
|
1237
|
-
};
|
|
1238
|
-
};
|
|
1239
1061
|
const sharedBlock = (layer_, options, inherited) => {
|
|
1240
1062
|
const blockEnv = blockEnvOf(inherited);
|
|
1241
1063
|
const memoMap = memoMapOf(options);
|
|
1242
1064
|
const scope = Scope.makeUnsafe();
|
|
1243
1065
|
const shared = mergeTestEnv(layer_(blockEnv), testEnv(blockEnv), excludeTestServicesOf(options));
|
|
1244
1066
|
const contextEffect = sharedContext(shared, memoMap, scope);
|
|
1245
|
-
const
|
|
1067
|
+
const nestedOpen = (nestedLayer, nestedOptions) => layerFor(() => Layer.provideMerge(nestedLayer, shared), nestedBlock(nestedOptions, memoMap), blockEnv);
|
|
1068
|
+
const nestedLayer = Function.dual(byLayer, nestedOpen);
|
|
1069
|
+
const makeIt = (it) => methodsFor((effect) => Effect.flatMap(contextEffect, (context) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, context))), it, () => blockEnv, false, nestedLayer);
|
|
1246
1070
|
const closeScope = makeScopeCloser(scope);
|
|
1247
1071
|
return blockOpener((f) => sharedAnonymous(f, makeIt, options, blockTasksOf(), blockEnv, contextEffect, closeScope), (name, f) => describeShared(name, f, makeIt, options, contextEffect, closeScope, blockEnv));
|
|
1248
1072
|
};
|
|
@@ -1253,8 +1077,6 @@ const describeShared = (name, f, makeIt, options, contextEffect, closeScope, blo
|
|
|
1253
1077
|
return f(makeIt(V.it));
|
|
1254
1078
|
});
|
|
1255
1079
|
};
|
|
1256
|
-
const blockTasksOf = () => suiteTasks(getCurrentSuite());
|
|
1257
|
-
const suiteTasks = (suite) => suite.tasks;
|
|
1258
1080
|
const sharedAnonymous = (register, makeIt, options, blockTasks, blockEnv, contextEffect, closeScope) => {
|
|
1259
1081
|
const previousTasks = new Set(blockTasks);
|
|
1260
1082
|
register(makeIt(V.it));
|
|
@@ -1282,20 +1104,6 @@ const sharedBeforeEach = (ctx, blockTaskSet, finish, contextEffect, blockEnv) =>
|
|
|
1282
1104
|
ctx.onTestFinished(() => finish());
|
|
1283
1105
|
return runPromise(Effect.asVoid(contextEffect), ctx, blockEnv);
|
|
1284
1106
|
};
|
|
1285
|
-
const collectTasks = (tasks) => {
|
|
1286
|
-
const acc = [];
|
|
1287
|
-
for (const task of tasks) collectOne(task, acc);
|
|
1288
|
-
return acc;
|
|
1289
|
-
};
|
|
1290
|
-
const collectOne = (task, acc) => {
|
|
1291
|
-
if (isRunnableTest(task)) acc.push(task);
|
|
1292
|
-
else collectNested(task, acc);
|
|
1293
|
-
};
|
|
1294
|
-
const collectNested = (task, acc) => {
|
|
1295
|
-
if (task.tasks !== void 0) collectTasks(task.tasks).forEach((nested) => acc.push(nested));
|
|
1296
|
-
};
|
|
1297
|
-
const notSkipped = (task) => task.mode !== "skip" && task.mode !== "todo";
|
|
1298
|
-
const isRunnableTest = (task) => task.type === "test" && notSkipped(task);
|
|
1299
1107
|
const layerImpl = (layer_, options) => layerFor(() => layer_, options, void 0);
|
|
1300
1108
|
/** @internal */
|
|
1301
1109
|
const layer$1 = Function.dual(byLayer, layerImpl);
|
|
@@ -1304,82 +1112,101 @@ const retryWithin = (timeout) => Schedule.while(Schedule.recurs(10), (state) =>
|
|
|
1304
1112
|
const flakyTestImpl = (self, timeout = Duration.seconds(30)) => self.pipe(Effect.scoped, Effect.sandbox, (sandboxed) => Effect.retry(sandboxed, retryWithin(timeout)), Effect.orDie);
|
|
1305
1113
|
/** @internal */
|
|
1306
1114
|
const flakyTest$1 = Function.dual(byEffect, flakyTestImpl);
|
|
1307
|
-
const isFunction = (arg) => typeof arg === "function";
|
|
1308
1115
|
/**
|
|
1309
|
-
* The
|
|
1310
|
-
*
|
|
1116
|
+
* The lanes a removed habit name reaches: the call is refused with its rewrite, on the `this` type and at run
|
|
1117
|
+
* time, and the property the lane used to carry still runs (R9, KTD3).
|
|
1311
1118
|
*/
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
if (outputIsEffect(output)) throw new Slop({ detail: refuseBareEffect });
|
|
1315
|
-
};
|
|
1316
|
-
/** One pass of the bare body: it runs bound to the run's context, and its return is refused here. */
|
|
1317
|
-
const bareRunOnce = (ctx, self, shadow) => {
|
|
1318
|
-
const returned = withSyncRun({
|
|
1319
|
-
ctx,
|
|
1320
|
-
shadow
|
|
1321
|
-
}, () => self(ctx));
|
|
1322
|
-
if (isObject(returned)) refuseBareOutput(returned);
|
|
1323
|
-
};
|
|
1324
|
-
/** The bare body's outcome: `undefined` when it passed, or the failure it threw or was refused with. */
|
|
1325
|
-
const bareOutcome = (ctx, self, shadow) => outcomeOf(Promise.resolve().then(() => bareRunOnce(ctx, self, shadow)));
|
|
1326
|
-
const shouldRerunBare = (ctx, rerunnable) => rerunnable && isClean(ctx);
|
|
1327
|
-
const afterBareGate = (ctx, self, rerunnable) => shouldRerunBare(ctx, rerunnable) ? bareOutcome(ctx, self, true).then(leakedOrNothing) : Promise.resolve();
|
|
1328
|
-
const afterBareFirst = (ctx, self, rerunnable, failure) => {
|
|
1329
|
-
if (failure !== void 0) throwAfterCheck(ctx, failure);
|
|
1330
|
-
gateNoAssertion(ctx);
|
|
1331
|
-
return afterBareGate(ctx, self, rerunnable);
|
|
1119
|
+
const refuseHabitLane = (text) => {
|
|
1120
|
+
throw refusalOf$1(text);
|
|
1332
1121
|
};
|
|
1122
|
+
const effectLaneRefusal = (prop, text) => Object.assign(() => refuseHabitLane(text), { prop });
|
|
1333
1123
|
/**
|
|
1334
|
-
* The
|
|
1335
|
-
* lane
|
|
1336
|
-
* which is what catches a body that only passes while nothing else touched its module (R5, R9).
|
|
1124
|
+
* The lanes over one `mapEffect`: `it`, its modifiers and `it.each` all register the same generator body, and
|
|
1125
|
+
* the Effect-lane property is bound to the same services the lane provides (R7, R9).
|
|
1337
1126
|
*/
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1127
|
+
const makeTesterWith = (mapEffect, it, envFor, rerunnable) => {
|
|
1128
|
+
const runOnce = (ctx, body) => runLaned(ctx, mapEffect, envFor(), body);
|
|
1129
|
+
const rerun = (ctx, body) => outcomeOf(runOnce(ctx, body)).then(leakedOrNothing);
|
|
1130
|
+
const shouldRerun = (ctx) => rerunnable && isClean(ctx);
|
|
1131
|
+
const rerunIfClean = (ctx, body) => shouldRerun(ctx) ? rerun(ctx, body) : Promise.resolve();
|
|
1132
|
+
const run = (ctx, body) => outcomeOf(runOnce(ctx, body)).then((error) => {
|
|
1133
|
+
if (error !== void 0) throw error;
|
|
1134
|
+
return rerunIfClean(ctx, body);
|
|
1135
|
+
});
|
|
1136
|
+
const registering = (registrar) => (name, body, timeout) => {
|
|
1137
|
+
const lane = laneBodyOf(body);
|
|
1138
|
+
marked(() => registrar(name, testOptions(timeout), (ctx) => run(ctx, lane)));
|
|
1139
|
+
};
|
|
1140
|
+
const dualLane = (registrar) => Function.dual(byName, registering(registrar));
|
|
1141
|
+
const base = dualLane(it);
|
|
1142
|
+
const skip = dualLane(it.skip);
|
|
1143
|
+
const skipIf = (condition) => dualLane(it.skipIf(condition));
|
|
1144
|
+
const runIf = (condition) => dualLane(it.runIf(condition));
|
|
1145
|
+
const only = dualLane(it.only);
|
|
1146
|
+
const fails = dualLane(it.fails);
|
|
1147
|
+
const each = (cases) => (name, body, timeout) => {
|
|
1148
|
+
marked(() => it.for(cases)(name, testOptions(timeout), (row, ctx) => run(ctx, rowBodyOf(row, body))));
|
|
1149
|
+
};
|
|
1150
|
+
const effectProperty = makeProperty({
|
|
1151
|
+
register: (name, program) => marked(() => it(name, (ctx) => runProperty(ctx, program))),
|
|
1152
|
+
provide: (effect) => mapEffect(effect, envFor())
|
|
1153
|
+
});
|
|
1154
|
+
const prop = (name, spec, holds) => {
|
|
1155
|
+
refusePositional(spec);
|
|
1156
|
+
effectProperty.effectProp(name, spec, holds);
|
|
1157
|
+
};
|
|
1158
|
+
return Object.assign(base, {
|
|
1159
|
+
skip,
|
|
1160
|
+
skipIf,
|
|
1161
|
+
runIf,
|
|
1162
|
+
only,
|
|
1163
|
+
fails,
|
|
1164
|
+
each,
|
|
1165
|
+
prop
|
|
1166
|
+
});
|
|
1167
|
+
};
|
|
1168
|
+
/** The block's methods: the generator lanes, the refusing habit lanes, and the block's own nested `layer`. */
|
|
1169
|
+
const methodsFor = (mapEffect, it, envFor, rerunnable, layer) => {
|
|
1170
|
+
const tester = makeTesterWith(mapEffect, it, envFor, rerunnable);
|
|
1171
|
+
return Object.assign(tester, {
|
|
1172
|
+
effect: effectLaneRefusal(tester.prop, refuseEffectLane),
|
|
1173
|
+
scoped: effectLaneRefusal(tester.prop, refuseScopedLane),
|
|
1174
|
+
scopedLive: effectLaneRefusal(tester.prop, refuseScopedLiveLane),
|
|
1175
|
+
prop: prop$1,
|
|
1176
|
+
law: property.law,
|
|
1177
|
+
flakyTest: flakyTest$1,
|
|
1178
|
+
layer
|
|
1179
|
+
});
|
|
1180
|
+
};
|
|
1181
|
+
const refuseHookNow = () => {
|
|
1182
|
+
throw refusalOf$1(refuseHook);
|
|
1183
|
+
};
|
|
1184
|
+
const takesHook = (args) => typeof args[0] === "function";
|
|
1185
|
+
const refuseHookPair = (fn, timeout) => {
|
|
1186
|
+
return refuseHookNow();
|
|
1187
|
+
};
|
|
1340
1188
|
/**
|
|
1341
|
-
* The fork's `it`: Vitest's test API with the fork's Effect lanes and lawful property lanes attached,
|
|
1342
|
-
* callable data-first (`it(name, body)`) or data-last (`it(body, options?)(name)`).
|
|
1343
|
-
*
|
|
1344
1189
|
* @internal
|
|
1345
1190
|
*/
|
|
1346
|
-
const
|
|
1347
|
-
effect: makeTester((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, testEnv(env))), it),
|
|
1348
|
-
live: makeTesterWith((effect) => Effect.scoped(effect), it, () => realTime, true),
|
|
1349
|
-
flakyTest: flakyTest$1,
|
|
1350
|
-
layer: layer$1,
|
|
1351
|
-
prop: prop$1,
|
|
1352
|
-
law: property.law
|
|
1353
|
-
}, true);
|
|
1354
|
-
/** @internal */
|
|
1355
|
-
const { effect: effect$1, live: live$1 } = makeMethods$1(V.it);
|
|
1356
|
-
function makeItProxy(it, overrides, rerunnable) {
|
|
1357
|
-
return new Proxy(it, {
|
|
1358
|
-
apply: (target, thisArg, args) => routeCall(target, thisArg, args, rerunnable),
|
|
1359
|
-
get: (target, property, receiver) => memberOrOverride(target, overrides, property, receiver)
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1362
|
-
function routeCall(target, thisArg, args, rerunnable) {
|
|
1363
|
-
return isName(args[0]) ? applied(target, thisArg, args, rerunnable) : (name) => applied(target, thisArg, [
|
|
1364
|
-
name,
|
|
1365
|
-
args[0],
|
|
1366
|
-
args[1]
|
|
1367
|
-
], rerunnable);
|
|
1368
|
-
}
|
|
1191
|
+
const beforeEach$2 = Function.dual(takesHook, refuseHookPair);
|
|
1369
1192
|
/**
|
|
1370
|
-
*
|
|
1371
|
-
* so the body is the argument that is a function, and it is the one the fork's bare lane wraps. Vitest's own
|
|
1372
|
-
* helpers (`it.skip`, `it.each`, ...) come through the proxy's `get` trap and are handed over untouched.
|
|
1193
|
+
* @internal
|
|
1373
1194
|
*/
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1195
|
+
const afterEach$1 = Function.dual(takesHook, refuseHookPair);
|
|
1196
|
+
/**
|
|
1197
|
+
* The fork's `it`: the generator lanes on virtual time, the real-clock lane, the lawful property lanes and the
|
|
1198
|
+
* layer blocks, callable data-first (`it(name, body)`) or data-last (`it(body, timeout?)(name)`).
|
|
1199
|
+
*
|
|
1200
|
+
* @internal
|
|
1201
|
+
*/
|
|
1202
|
+
const makeMethods$1 = (it) => {
|
|
1203
|
+
const base = methodsFor((effect, env) => effect.pipe(Effect.scoped, (scoped) => Effect.provide(scoped, testEnv(env))), it, perRun, true, layer$1);
|
|
1204
|
+
const live = makeTesterWith((effect) => Effect.scoped(effect), it, () => realTime, true);
|
|
1205
|
+
return Object.assign(base, {
|
|
1206
|
+
live,
|
|
1207
|
+
layer: layer$1
|
|
1208
|
+
});
|
|
1209
|
+
};
|
|
1383
1210
|
const describeImpl = (name, f) => V.describe(name, {
|
|
1384
1211
|
concurrent: true,
|
|
1385
1212
|
shuffle: true
|
|
@@ -1389,60 +1216,50 @@ const describeWrapped$1 = Function.dual(byName, describeImpl);
|
|
|
1389
1216
|
//#endregion
|
|
1390
1217
|
//#region src/mod.ts
|
|
1391
1218
|
/**
|
|
1219
|
+
* The fork's test API, with the generator lanes of R2 and R9.
|
|
1220
|
+
*
|
|
1392
1221
|
* @since 4.0.0
|
|
1393
1222
|
*/
|
|
1394
|
-
const
|
|
1223
|
+
const it = makeMethods$1(V.it);
|
|
1395
1224
|
/**
|
|
1225
|
+
* The same lanes under the other name upstream publishes.
|
|
1226
|
+
*
|
|
1396
1227
|
* @since 4.0.0
|
|
1397
1228
|
*/
|
|
1398
|
-
const
|
|
1229
|
+
const test = it;
|
|
1399
1230
|
/**
|
|
1231
|
+
* The fork's lawful collector: it forces the fork's concurrency and shuffle defaults and hands its body the
|
|
1232
|
+
* fork's methods.
|
|
1233
|
+
*
|
|
1400
1234
|
* @since 4.0.0
|
|
1401
1235
|
*/
|
|
1402
|
-
const
|
|
1236
|
+
const describeWrapped = describeWrapped$1;
|
|
1403
1237
|
/**
|
|
1404
|
-
* Share a `Layer` between multiple tests, optionally wrapping
|
|
1405
|
-
* the tests in a `describe` block if a name is provided.
|
|
1406
|
-
*
|
|
1407
|
-
* Named layers accept `concurrent` to override inherited suite concurrency.
|
|
1408
|
-
* Anonymous layers always inherit the enclosing suite's concurrency.
|
|
1409
|
-
* Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts.
|
|
1410
|
-
*
|
|
1411
1238
|
* @since 4.0.0
|
|
1239
|
+
*/
|
|
1240
|
+
const describe = describeWrapped$1;
|
|
1241
|
+
/**
|
|
1242
|
+
* Share a `Layer` between multiple tests, optionally wrapping the tests in a `describe` block if a name is
|
|
1243
|
+
* provided. Every test in the block gets its own fresh build of the layer, unless the block declares
|
|
1244
|
+
* `shared: true`.
|
|
1412
1245
|
*
|
|
1413
1246
|
* ```ts
|
|
1414
|
-
* import {
|
|
1415
|
-
* import { Effect, Layer
|
|
1247
|
+
* import { it, layer } from "@systemfsoftware/vitest"
|
|
1248
|
+
* import { Context, Effect, Layer } from "effect"
|
|
1416
1249
|
*
|
|
1417
1250
|
* class Foo extends Context.Service<Foo, "foo">()("Foo") {
|
|
1418
1251
|
* static layer = Layer.succeed(Foo, "foo")
|
|
1419
1252
|
* }
|
|
1420
1253
|
*
|
|
1421
|
-
* class Bar extends Context.Service<Bar, "bar">()("Bar") {
|
|
1422
|
-
* static layer = Layer.effect(
|
|
1423
|
-
* Bar,
|
|
1424
|
-
* Effect.map(Foo, () => "bar" as const)
|
|
1425
|
-
* )
|
|
1426
|
-
* }
|
|
1427
|
-
*
|
|
1428
1254
|
* layer(Foo.layer)("layer", (it) => {
|
|
1429
|
-
* it
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1432
|
-
* assert.strictEqual(foo, "foo")
|
|
1433
|
-
* }))
|
|
1434
|
-
*
|
|
1435
|
-
* it.layer(Bar.layer)("nested", (it) => {
|
|
1436
|
-
* it.effect("adds context", () =>
|
|
1437
|
-
* Effect.gen(function*() {
|
|
1438
|
-
* const foo = yield* Foo
|
|
1439
|
-
* const bar = yield* Bar
|
|
1440
|
-
* assert.strictEqual(foo, "foo")
|
|
1441
|
-
* assert.strictEqual(bar, "bar")
|
|
1442
|
-
* }))
|
|
1255
|
+
* it("reads the service", function* ({ expect }) {
|
|
1256
|
+
* const foo = yield* Foo
|
|
1257
|
+
* yield* expect(foo).toEqual("foo")
|
|
1443
1258
|
* })
|
|
1444
1259
|
* })
|
|
1445
1260
|
* ```
|
|
1261
|
+
*
|
|
1262
|
+
* @since 4.0.0
|
|
1446
1263
|
*/
|
|
1447
1264
|
const layer = layer$1;
|
|
1448
1265
|
/**
|
|
@@ -1456,68 +1273,42 @@ const prop = prop$1;
|
|
|
1456
1273
|
/**
|
|
1457
1274
|
* @since 4.0.0
|
|
1458
1275
|
*/
|
|
1459
|
-
const
|
|
1460
|
-
/**
|
|
1461
|
-
* @since 4.0.0
|
|
1462
|
-
*/
|
|
1463
|
-
const recordAssertion = recordAssertion$1;
|
|
1276
|
+
const law = law$1;
|
|
1464
1277
|
/**
|
|
1465
|
-
* The
|
|
1466
|
-
* to an effect a library runs on a runtime of its own — its own scheduler, a
|
|
1467
|
-
* worker, a simulation kernel — so the checks inside it count as this test's
|
|
1468
|
-
* assertions, report softly, and see the same `owned` regions as the test.
|
|
1278
|
+
* The fork's test API over another Vitest `it`, so a library can register tests in its own suite.
|
|
1469
1279
|
*
|
|
1470
1280
|
* @since 4.0.0
|
|
1471
1281
|
*/
|
|
1472
|
-
const
|
|
1282
|
+
const makeMethods = makeMethods$1;
|
|
1473
1283
|
/**
|
|
1474
|
-
*
|
|
1475
|
-
* context builds that view on this key, so the context a case lane sees and a property lane sees agree.
|
|
1284
|
+
* Refused: hooks share state between tests, and services come fresh per test from `layer`.
|
|
1476
1285
|
*
|
|
1477
1286
|
* @since 4.0.0
|
|
1478
1287
|
*/
|
|
1479
|
-
const
|
|
1288
|
+
const beforeEach = beforeEach$2;
|
|
1480
1289
|
/**
|
|
1481
|
-
*
|
|
1482
|
-
* lanes, and each `it.each` row. It is `null` outside a run.
|
|
1290
|
+
* Refused: hooks share state between tests, and services come fresh per test from `layer`.
|
|
1483
1291
|
*
|
|
1484
1292
|
* @since 4.0.0
|
|
1485
1293
|
*/
|
|
1486
|
-
const
|
|
1487
|
-
/**
|
|
1488
|
-
* @since 4.0.0
|
|
1489
|
-
*/
|
|
1490
|
-
const it = makeMethods$1(V.it);
|
|
1491
|
-
/**
|
|
1492
|
-
* @since 4.0.0
|
|
1493
|
-
*/
|
|
1494
|
-
const makeMethods = makeMethods$1;
|
|
1294
|
+
const afterEach = afterEach$1;
|
|
1495
1295
|
/**
|
|
1496
1296
|
* @since 4.0.0
|
|
1497
1297
|
*/
|
|
1498
|
-
const
|
|
1499
|
-
const describe = describeWrapped$1;
|
|
1298
|
+
const addEqualityTesters = addEqualityTesters$1;
|
|
1500
1299
|
/**
|
|
1501
|
-
* The
|
|
1300
|
+
* The key the running test's context is published under. A library that carries its own view of the task
|
|
1301
|
+
* context builds that view on this key, so the context a case lane sees and a property lane sees agree.
|
|
1502
1302
|
*
|
|
1503
1303
|
* @since 4.0.0
|
|
1504
1304
|
*/
|
|
1505
|
-
const
|
|
1506
|
-
/**
|
|
1507
|
-
* @since 4.0.0
|
|
1508
|
-
*/
|
|
1509
|
-
const beforeEach = beforeEach$1;
|
|
1510
|
-
/**
|
|
1511
|
-
* @since 4.0.0
|
|
1512
|
-
*/
|
|
1513
|
-
const afterEach = afterEach$1;
|
|
1514
|
-
/**
|
|
1515
|
-
* @since 4.0.0
|
|
1516
|
-
*/
|
|
1517
|
-
const beforeAll = beforeAll$1;
|
|
1305
|
+
const vitestTestContextKey = vitestTestContextKey$1;
|
|
1518
1306
|
/**
|
|
1307
|
+
* The running Vitest `TestContext`, provided on every test the fork runs — every generator lane, both property
|
|
1308
|
+
* lanes, and each `it.each` row. It is `null` outside a run.
|
|
1309
|
+
*
|
|
1519
1310
|
* @since 4.0.0
|
|
1520
1311
|
*/
|
|
1521
|
-
const
|
|
1312
|
+
const VitestTestContext = VitestTestContext$1;
|
|
1522
1313
|
//#endregion
|
|
1523
|
-
export { VitestTestContext, addEqualityTesters, afterAll, afterEach, beforeAll, beforeEach,
|
|
1314
|
+
export { VitestTestContext, addEqualityTesters, afterAll, afterEach, assertType, beforeAll, beforeEach, describe, describeWrapped, expectTypeOf, flakyTest, inject, it, law, layer, makeMethods, onTestFailed, onTestFinished, prop, recordArtifact, test, vi, vitest, vitestTestContextKey };
|