1/**
2 * @since 4.0.0
3 */
4import type * as Arbitrary from "effect/Arbitrary"
5import type * as Duration from "effect/Duration"
6import type * as Effect from "effect/Effect"
7import type * as Layer from "effect/Layer"
8import type * as Schema from "effect/Schema"
9import type * as Scope from "effect/Scope"
10import type * as V from "vitest"
11import * as internal from "./internal/internal.ts"
12
13/**
14 * Re-exports the `vitest` API unchanged.
15 *
16 * @stability unstable
17 * @since 4.0.0
18 */
19export * from "vitest"
20
21/**
22 * @stability unstable
23 * @since 4.0.0
24 */
25export type API = V.TestAPI<{}>
26
27/**
28 * @since 4.0.0
29 */
30export namespace Vitest {
31 /**
32 * @since 4.0.0
33 */
34 export interface TestFunction<A, E, R, TestArgs extends Array<any>> {
35 (...args: TestArgs): Effect.Effect<A, E, R>
36 }
37
38 /**
39 * @since 4.0.0
40 */
41 export interface Test<R, ExtraContext = {}> {
42 <A, E>(
43 name: string,
44 self: TestFunction<A, E, R, [V.TestContext & ExtraContext]>,
45 timeout?: number | V.TestOptions
46 ): void
47 }
48
49 /**
50 * @since 4.0.0
51 */
52 export type Arbitraries =
53 | Array<Schema.Schema<any> | Arbitrary.Arbitrary<any>>
54 | { [K in string]: Schema.Schema<any> | Arbitrary.Arbitrary<any> }
55
56 type ArbitraryValue<A> = A extends Schema.Schema<infer T> ? T
57 : A extends Arbitrary.Arbitrary<infer T> ? T
58 : never
59
60 /**
61 * @since 4.0.0
62 */
63 export interface Tester<R, ExtraContext = {}> extends Vitest.Test<R, ExtraContext> {
64 skip: Vitest.Test<R, ExtraContext>
65 skipIf: (condition: unknown) => Vitest.Test<R, ExtraContext>
66 runIf: (condition: unknown) => Vitest.Test<R, ExtraContext>
67 only: Vitest.Test<R, ExtraContext>
68 each: <T>(
69 cases: ReadonlyArray<T>
70 ) => <A, E>(
71 name: string,
72 self: TestFunction<A, E, R, [T, V.TestContext & ExtraContext]>,
73 timeout?: number | V.TestOptions
74 ) => void
75 fails: Vitest.Test<R, ExtraContext>
76
77 /**
78 * Runs an Effectful property test using Schema or Arbitrary inputs.
79 *
80 * **Details**
81 *
82 * Returning `false` or completing with any non-interruption failure falsifies the property and triggers shrinking.
83 * This includes typed Effect failures, thrown exceptions, and defects such as failed assertions. Effect
84 * interruption continues to interrupt the test.
85 *
86 * The Vitest timeout interrupts the Effect fiber running generation, property evaluation, and shrinking. Effect
87 * finalizers run during that interruption.
88 *
89 * **Gotchas**
90 *
91 * A timeout cannot preempt a synchronous JavaScript callback that does not return.
92 *
93 * @since 4.0.0
94 */
95 prop: <const Arbs extends Arbitraries, A, E>(
96 name: string,
97 arbitraries: Arbs,
98 self: TestFunction<
99 A,
100 E,
101 R,
102 [
103 {
104 [K in keyof Arbs]: ArbitraryValue<Arbs[K]>
105 },
106 V.TestContext
107 ]
108 >,
109 timeout?:
110 | number
111 | V.TestOptions & {
112 arbitrary?: Arbitrary.CheckOptions
113 }
114 ) => void
115 }
116
117 /**
118 * @since 4.0.0
119 */
120 export interface MethodsNonLive<R = never, ExtraContext = {}> extends V.TestAPI<ExtraContext> {
121 readonly effect: Vitest.Tester<R | Scope.Scope, ExtraContext>
122 readonly flakyTest: <A, E, R2>(
123 self: Effect.Effect<A, E, R2 | Scope.Scope>,
124 timeout?: Duration.Input
125 ) => Effect.Effect<A, never, R2>
126 readonly layer: <R2, E>(layer: Layer.Layer<R2, E, R>, options?: {
127 readonly concurrent?: boolean
128 readonly timeout?: Duration.Input
129 }) => {
130 (f: (it: Vitest.MethodsNonLive<R | R2, ExtraContext>) => void): void
131 (
132 name: string,
133 f: (it: Vitest.MethodsNonLive<R | R2, ExtraContext>) => void
134 ): void
135 }
136
137 /**
138 * Runs a synchronous property test using Schema or Arbitrary inputs.
139 *
140 * **Details**
141 *
142 * Returning `false` or throwing falsifies the property and triggers shrinking. A callback that returns normally
143 * without returning `false` passes for that generated input.
144 *
145 * The Vitest timeout interrupts the Effect fiber running generation and shrinking.
146 *
147 * **Gotchas**
148 *
149 * A timeout cannot preempt a synchronous JavaScript callback that does not return.
150 *
151 * @since 4.0.0
152 */
153 readonly prop: <const Arbs extends Arbitraries>(
154 name: string,
155 arbitraries: Arbs,
156 self: (
157 properties: {
158 [K in keyof Arbs]: ArbitraryValue<Arbs[K]>
159 },
160 ctx: V.TestContext
161 ) => void,
162 timeout?:
163 | number
164 | V.TestOptions & {
165 arbitrary?: Arbitrary.CheckOptions
166 }
167 ) => void
168 }
169
170 /**
171 * @since 4.0.0
172 */
173 export interface Methods<R = never, ExtraContext = {}> extends MethodsNonLive<R, ExtraContext> {
174 readonly live: Vitest.Tester<Scope.Scope | R, ExtraContext>
175 readonly layer: <R2, E>(layer: Layer.Layer<R2, E, R>, options?: {
176 readonly concurrent?: boolean
177 readonly memoMap?: Layer.MemoMap
178 readonly timeout?: Duration.Input
179 readonly excludeTestServices?: boolean
180 }) => {
181 (f: (it: Vitest.MethodsNonLive<R | R2, ExtraContext>) => void): void
182 (
183 name: string,
184 f: (it: Vitest.MethodsNonLive<R | R2, ExtraContext>) => void
185 ): void
186 }
187 }
188}
189
190/**
191 * @since 4.0.0
192 */
193export const addEqualityTesters: () => void = internal.addEqualityTesters
194
195/**
196 * @since 4.0.0
197 */
198export const effect: Vitest.Tester<Scope.Scope> = internal.effect
199
200/**
201 * @since 4.0.0
202 */
203export const live: Vitest.Tester<Scope.Scope> = internal.live
204
205/**
206 * Share a `Layer` between multiple tests, optionally wrapping
207 * the tests in a `describe` block if a name is provided.
208 *
209 * Named layers accept `concurrent` to override inherited suite concurrency.
210 * Anonymous layers always inherit the enclosing suite's concurrency.
211 * Use `ctx.expect` in concurrent tests for test-local snapshots and assertion counts.
212 *
213 * @since 4.0.0
214 *
215 * ```ts
216 * import { assert, layer } from "@effect/vitest"
217 * import { Effect, Layer, Context } from "effect"
218 *
219 * class Foo extends Context.Service<Foo, "foo">()("Foo") {
220 * static layer = Layer.succeed(Foo, "foo")
221 * }
222 *
223 * class Bar extends Context.Service<Bar, "bar">()("Bar") {
224 * static layer = Layer.effect(
225 * Bar,
226 * Effect.map(Foo, () => "bar" as const)
227 * )
228 * }
229 *
230 * layer(Foo.layer)("layer", (it) => {
231 * it.effect("adds context", () =>
232 * Effect.gen(function*() {
233 * const foo = yield* Foo
234 * assert.strictEqual(foo, "foo")
235 * }))
236 *
237 * it.layer(Bar.layer)("nested", (it) => {
238 * it.effect("adds context", () =>
239 * Effect.gen(function*() {
240 * const foo = yield* Foo
241 * const bar = yield* Bar
242 * assert.strictEqual(foo, "foo")
243 * assert.strictEqual(bar, "bar")
244 * }))
245 * })
246 * })
247 * ```
248 */
249export const layer: <R, E>(
250 layer_: Layer.Layer<R, E>,
251 options?: {
252 readonly concurrent?: boolean
253 readonly memoMap?: Layer.MemoMap
254 readonly timeout?: Duration.Input
255 readonly excludeTestServices?: boolean
256 }
257) => {
258 (f: (it: Vitest.MethodsNonLive<R>) => void): void
259 (name: string, f: (it: Vitest.MethodsNonLive<R>) => void): void
260} = internal.layer
261
262/**
263 * @since 4.0.0
264 */
265export const flakyTest: <A, E, R>(
266 self: Effect.Effect<A, E, R | Scope.Scope>,
267 timeout?: Duration.Input
268) => Effect.Effect<A, never, R> = internal.flakyTest
269
270/**
271 * @since 4.0.0
272 */
273export const prop: Vitest.Methods["prop"] = internal.prop
274
275/**
276 * @since 4.0.0
277 */
278
279/**
280 * @since 4.0.0
281 */
282export const it: Vitest.Methods = internal.it
283
284/**
285 * Creates the Effect test helpers for a Vitest test API, such as one extended with fixtures.
286 *
287 * **Details**
288 *
289 * Tests receive the fixtures they destructure from their context. Vitest sets them up before the test and tears
290 * them down after the test's scope closes, as it does for its own tests. `it.effect.each` passes the context
291 * after the test case, and named and anonymous `it.layer` blocks keep the fixtures.
292 *
293 * **Gotchas**
294 *
295 * Vitest reads the destructured names to decide which fixtures to set up. Once any fixture is defined, a test that
296 * takes the whole context as a plain parameter, such as `(ctx) =>`, fails with a `FixtureParseError`. Property
297 * tests receive only the base test context and cannot request fixtures; auto fixtures still run.
298 *
299 * **Example** (Using a Vitest fixture in an Effect test)
300 *
301 * ```ts
302 * import { assert, makeMethods, test } from "@effect/vitest"
303 * import { Effect } from "effect"
304 *
305 * const it = makeMethods(
306 * test.extend("config", { scope: "file" }, () => ({ port: 3000 }))
307 * )
308 *
309 * it.effect("reads the config fixture", ({ config }) =>
310 * Effect.sync(() => {
311 * assert.strictEqual(config.port, 3000)
312 * }))
313 * ```
314 *
315 * @since 4.0.0
316 */
317export const makeMethods: <ExtraContext>(it: V.TestAPI<ExtraContext>) => Vitest.Methods<never, ExtraContext> =
318 internal.makeMethods
319
320/**
321 * @since 4.0.0
322 */
323export const describeWrapped: (name: string, f: (it: Vitest.Methods) => void) => V.SuiteCollector =
324 internal.describeWrapped