From ba0ae13a7ac06f31221ec9db90c7527b2152dc3e Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 26 Sep 2024 17:23:58 +1200 Subject: [PATCH 1/6] add vitest layer api --- .changeset/dull-laws-joke.md | 5 ++++ packages/vitest/src/index.ts | 28 ++++++++++++++++++++-- packages/vitest/src/internal.ts | 37 ++++++++++++++++++++++++++++++ packages/vitest/test/index.test.ts | 33 ++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 .changeset/dull-laws-joke.md diff --git a/.changeset/dull-laws-joke.md b/.changeset/dull-laws-joke.md new file mode 100644 index 0000000000..cb3701011d --- /dev/null +++ b/.changeset/dull-laws-joke.md @@ -0,0 +1,5 @@ +--- +"@effect/vitest": patch +--- + +add vitest layer api diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index e1fee6e0a2..f7ca8dcc6a 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -3,6 +3,7 @@ */ import type * as Duration from "effect/Duration" import type * as Effect from "effect/Effect" +import type * as Layer from "effect/Layer" import type * as Scope from "effect/Scope" import type * as TestServices from "effect/TestServices" import * as V from "vitest" @@ -47,6 +48,21 @@ export namespace Vitest { cases: ReadonlyArray ) => (name: string, self: TestFunction>, timeout?: number | V.TestOptions) => void } + + /** + * @since 1.0.0 + */ + export interface Methods extends API { + readonly effect: Vitest.Tester + readonly live: Vitest.Tester + readonly flakyTest: ( + self: Effect.Effect, + timeout?: Duration.DurationInput + ) => Effect.Effect + readonly scoped: Vitest.Tester + readonly scopedLive: Vitest.Tester + readonly layer: (layer: Layer.Layer) => (f: (it: Vitest.Methods) => void) => void + } } /** * @since 1.0.0 @@ -73,6 +89,14 @@ export const live: Vitest.Tester = internal.live */ export const scopedLive: Vitest.Tester = internal.scopedLive +/** + * @since 1.0.0 + */ +export const layer: ( + layer_: Layer.Layer, + memoMap?: Layer.MemoMap +) => (f: (it: Vitest.Methods) => void) => void = internal.layer + /** * @since 1.0.0 */ @@ -82,12 +106,12 @@ export const flakyTest: ( ) => Effect.Effect = internal.flakyTest /** @ignored */ -const methods = { effect, live, flakyTest, scoped, scopedLive } as const +const methods = { effect, live, flakyTest, scoped, scopedLive, layer } as const /** * @since 1.0.0 */ -export const it: API & typeof methods = Object.assign(V.it, methods) +export const it: Vitest.Methods = Object.assign(V.it, methods) /** * @since 1.0.0 diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index 983f073c4d..846e6637ff 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -11,6 +11,7 @@ import * as Fiber from "effect/Fiber" import { flow, identity, pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" +import * as ManagedRuntime from "effect/ManagedRuntime" import * as Runtime from "effect/Runtime" import * as Schedule from "effect/Schedule" import type * as Scope from "effect/Scope" @@ -98,6 +99,42 @@ const makeTester = ( return Object.assign(f, { skip, skipIf, runIf, only, each }) } +/** @internal */ +export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap) => { + const runtime = ManagedRuntime.make(Layer.orDie(layer_), memoMap) + V.afterAll(() => runtime.dispose()) + + const it: Vitest.Vitest.Methods = Object.assign(V.it, { + effect: makeTester((effect) => + effect.pipe( + Effect.provide(runtime), + Effect.provide(TestEnv) + ) + ), + scoped: makeTester((effect) => + effect.pipe( + Effect.scoped, + Effect.provide(runtime), + Effect.provide(TestEnv) + ) + ), + live: makeTester((effect) => effect.pipe(Effect.provide(runtime))), + scopedLive: makeTester((effect) => effect.pipe(Effect.scoped, Effect.provide(runtime))), + flakyTest, + layer(nestedLayer: Layer.Layer) { + return layer( + nestedLayer.pipe( + Layer.provideMerge(layer_), + Layer.orDie + ), + runtime.memoMap + ) + } + }) + + return (f: (it: Vitest.Vitest.Methods) => void) => f(it) +} + /** @internal */ export const effect = makeTester(Effect.provide(TestEnv)) diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 6574aedec9..5c21e8c232 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -1,5 +1,5 @@ -import { expect, it } from "@effect/vitest" -import { Effect } from "effect" +import { expect, it, layer } from "@effect/vitest" +import { Context, Effect, Layer } from "effect" it.live( "live %s", @@ -85,3 +85,32 @@ it.scopedLive("interrupts on timeout", (ctx) => ) yield* Effect.sleep(1000) }), { timeout: 100, fails: true }) + +class Foo extends Context.Tag("Foo")() { + static Live = Layer.succeed(Foo, "foo") +} + +class Bar extends Context.Tag("Bar")() { + static Live = Layer.effect( + Bar, + Effect.map(Foo, () => "bar" as const) + ) +} + +layer(Foo.Live)((it) => { + it.effect("layer adds context", () => + Effect.gen(function*() { + const foo = yield* Foo + expect(foo).toEqual("foo") + })) + + it.layer(Bar.Live)((it) => { + it.effect("nested layer adds context", () => + Effect.gen(function*() { + const foo = yield* Foo + const bar = yield* Bar + expect(foo).toEqual("foo") + expect(bar).toEqual("bar") + })) + }) +}) From f678c4cc11bc30c81489ac8726de5e31e927229e Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 26 Sep 2024 21:30:11 +1200 Subject: [PATCH 2/6] warm up and name --- packages/vitest/src/index.ts | 11 +++++-- packages/vitest/src/internal.ts | 27 +++++++++++------- packages/vitest/test/index.test.ts | 46 +++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index f7ca8dcc6a..09e1db8c9e 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -61,9 +61,13 @@ export namespace Vitest { ) => Effect.Effect readonly scoped: Vitest.Tester readonly scopedLive: Vitest.Tester - readonly layer: (layer: Layer.Layer) => (f: (it: Vitest.Methods) => void) => void + readonly layer: (layer: Layer.Layer) => { + (f: (it: Vitest.Methods) => void): void + (name: string, f: (it: Vitest.Methods) => void): void + } } } + /** * @since 1.0.0 */ @@ -95,7 +99,10 @@ export const scopedLive: Vitest.Tester = internal.scopedLive export const layer: ( layer_: Layer.Layer, memoMap?: Layer.MemoMap -) => (f: (it: Vitest.Methods) => void) => void = internal.layer +) => { + (f: (it: Vitest.Methods) => void): void + (name: string, f: (it: Vitest.Methods) => void): void +} = internal.layer /** * @since 1.0.0 diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index 846e6637ff..7fc683a188 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect" import * as Equal from "effect/Equal" import * as Exit from "effect/Exit" import * as Fiber from "effect/Fiber" -import { flow, identity, pipe } from "effect/Function" +import { constVoid, flow, identity, pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" import * as ManagedRuntime from "effect/ManagedRuntime" @@ -100,8 +100,12 @@ const makeTester = ( } /** @internal */ -export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap) => { +export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap): { + (f: (it: Vitest.Vitest.Methods) => void): void + (name: string, f: (it: Vitest.Vitest.Methods) => void): void +} => { const runtime = ManagedRuntime.make(Layer.orDie(layer_), memoMap) + V.beforeAll(() => runtime.runtime().then(constVoid)) V.afterAll(() => runtime.dispose()) const it: Vitest.Vitest.Methods = Object.assign(V.it, { @@ -118,21 +122,22 @@ export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap) Effect.provide(TestEnv) ) ), - live: makeTester((effect) => effect.pipe(Effect.provide(runtime))), + live: makeTester(Effect.provide(runtime)), scopedLive: makeTester((effect) => effect.pipe(Effect.scoped, Effect.provide(runtime))), flakyTest, layer(nestedLayer: Layer.Layer) { - return layer( - nestedLayer.pipe( - Layer.provideMerge(layer_), - Layer.orDie - ), - runtime.memoMap - ) + return layer(Layer.provideMerge(nestedLayer, layer_), runtime.memoMap) } }) - return (f: (it: Vitest.Vitest.Methods) => void) => f(it) + return function( + ...args: [name: string, f: (it: Vitest.Vitest.Methods) => void] | [f: (it: Vitest.Vitest.Methods) => void] + ) { + if (args.length === 1) { + return args[0](it) + } + return V.describe(args[0], () => args[1](it)) + } } /** @internal */ diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 5c21e8c232..02638d8fa5 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -1,5 +1,6 @@ -import { expect, it, layer } from "@effect/vitest" +import { afterAll, expect, it, layer } from "@effect/vitest" import { Context, Effect, Layer } from "effect" +import { describe } from "node:test" it.live( "live %s", @@ -97,15 +98,25 @@ class Bar extends Context.Tag("Bar")() { ) } -layer(Foo.Live)((it) => { - it.effect("layer adds context", () => +layer(Foo.Live)("layer", (it) => { + it.effect("adds context", () => Effect.gen(function*() { const foo = yield* Foo expect(foo).toEqual("foo") })) + it.layer(Bar.Live)("nested", (it) => { + it.effect("adds context", () => + Effect.gen(function*() { + const foo = yield* Foo + const bar = yield* Bar + expect(foo).toEqual("foo") + expect(bar).toEqual("bar") + })) + }) + it.layer(Bar.Live)((it) => { - it.effect("nested layer adds context", () => + it.effect("without name", () => Effect.gen(function*() { const foo = yield* Foo const bar = yield* Bar @@ -113,4 +124,31 @@ layer(Foo.Live)((it) => { expect(bar).toEqual("bar") })) }) + + describe("releases", () => { + let released = false + afterAll(() => { + expect(released).toEqual(true) + }) + + class Scoped extends Context.Tag("Scoped")() { + static Live = Layer.scoped( + Scoped, + Effect.acquireRelease( + Effect.succeed("scoped" as const), + () => Effect.sync(() => released = true) + ) + ) + } + + it.layer(Scoped.Live)((it) => { + it.effect("adds context", () => + Effect.gen(function*() { + const foo = yield* Foo + const scoped = yield* Scoped + expect(foo).toEqual("foo") + expect(scoped).toEqual("scoped") + })) + }) + }) }) From 2d0a480e47fbf83f12eed0c9b8f81573e1d8935f Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 26 Sep 2024 22:52:14 +1200 Subject: [PATCH 3/6] remove ManagedRuntime --- packages/vitest/src/internal.ts | 72 +++++++++++++++++++----------- packages/vitest/test/index.test.ts | 2 +- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index 7fc683a188..7823f6b1a3 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -8,35 +8,31 @@ import * as Effect from "effect/Effect" import * as Equal from "effect/Equal" import * as Exit from "effect/Exit" import * as Fiber from "effect/Fiber" -import { constVoid, flow, identity, pipe } from "effect/Function" +import { flow, identity, pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" -import * as ManagedRuntime from "effect/ManagedRuntime" -import * as Runtime from "effect/Runtime" import * as Schedule from "effect/Schedule" -import type * as Scope from "effect/Scope" +import * as Scope from "effect/Scope" import * as TestEnvironment from "effect/TestContext" import type * as TestServices from "effect/TestServices" import * as Utils from "effect/Utils" import * as V from "vitest" import type * as Vitest from "./index.js" -/** @internal */ -const runTest = (ctx: Vitest.TaskContext) => (effect: Effect.Effect) => +const runPromise = (ctx?: Vitest.TaskContext) => (effect: Effect.Effect) => Effect.gen(function*() { - const exitFiber = yield* Effect.fork(Effect.exit(effect)) - const runtime = yield* Effect.runtime() + const exitFiber = yield* Effect.fork(effect) - ctx.onTestFinished(() => + ctx?.onTestFinished(() => Fiber.interrupt(exitFiber).pipe( Effect.asVoid, - Runtime.runPromise(runtime) + Effect.runPromise ) ) - const exit = yield* Fiber.join(exitFiber) + const exit = yield* exitFiber.await if (Exit.isSuccess(exit)) { - return () => {} + return () => exit.value } else { const errors = Cause.prettyErrors(exit.cause) for (let i = 1; i < errors.length; i++) { @@ -48,6 +44,10 @@ const runTest = (ctx: Vitest.TaskContext) => (effect: Effect.Effect) } }).pipe(Effect.runPromise).then((f) => f()) +/** @internal */ +const runTest = (ctx?: Vitest.TaskContext) => (effect: Effect.Effect) => + runPromise(ctx)(Effect.asVoid(effect)) + /** @internal */ const TestEnv = TestEnvironment.TestContext.pipe( Layer.provide(Logger.remove(Logger.defaultLogger)) @@ -104,29 +104,49 @@ export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap): (f: (it: Vitest.Vitest.Methods) => void): void (name: string, f: (it: Vitest.Vitest.Methods) => void): void } => { - const runtime = ManagedRuntime.make(Layer.orDie(layer_), memoMap) - V.beforeAll(() => runtime.runtime().then(constVoid)) - V.afterAll(() => runtime.dispose()) + memoMap = memoMap ?? Effect.runSync(Layer.makeMemoMap) + const scope = Effect.runSync(Scope.make()) + const runtimeEffect = Layer.toRuntimeWithMemoMap(layer_, memoMap).pipe( + Scope.extend(scope), + Effect.orDie, + Effect.cached, + Effect.runSync + ) + V.beforeAll(() => runPromise()(Effect.asVoid(runtimeEffect))) + V.afterAll(() => runPromise()(Scope.close(scope, Exit.void))) const it: Vitest.Vitest.Methods = Object.assign(V.it, { effect: makeTester((effect) => - effect.pipe( - Effect.provide(runtime), - Effect.provide(TestEnv) - ) + Effect.flatMap(runtimeEffect, (runtime) => + effect.pipe( + Effect.provide(runtime), + Effect.provide(TestEnv) + )) ), scoped: makeTester((effect) => - effect.pipe( - Effect.scoped, - Effect.provide(runtime), - Effect.provide(TestEnv) + Effect.flatMap(runtimeEffect, (runtime) => + effect.pipe( + Effect.scoped, + Effect.provide(runtime), + Effect.provide(TestEnv) + )) + ), + live: makeTester((effect) => + Effect.flatMap( + runtimeEffect, + (runtime) => Effect.provide(effect, runtime) ) ), - live: makeTester(Effect.provide(runtime)), - scopedLive: makeTester((effect) => effect.pipe(Effect.scoped, Effect.provide(runtime))), + scopedLive: makeTester((effect) => + Effect.flatMap(runtimeEffect, (runtime) => + effect.pipe( + Effect.scoped, + Effect.provide(runtime) + )) + ), flakyTest, layer(nestedLayer: Layer.Layer) { - return layer(Layer.provideMerge(nestedLayer, layer_), runtime.memoMap) + return layer(Layer.provideMerge(nestedLayer, layer_), memoMap) } }) diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 02638d8fa5..75eda0ce0a 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -125,7 +125,7 @@ layer(Foo.Live)("layer", (it) => { })) }) - describe("releases", () => { + describe("release", () => { let released = false afterAll(() => { expect(released).toEqual(true) From b652a19cb4ad91b050139f8bc06d9ff5e629f39f Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 27 Sep 2024 09:16:28 +1200 Subject: [PATCH 4/6] prevent unhandled errors --- packages/vitest/src/internal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index 7823f6b1a3..02c91ba808 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -21,7 +21,7 @@ import type * as Vitest from "./index.js" const runPromise = (ctx?: Vitest.TaskContext) => (effect: Effect.Effect) => Effect.gen(function*() { - const exitFiber = yield* Effect.fork(effect) + const exitFiber = yield* Effect.fork(Effect.exit(effect)) ctx?.onTestFinished(() => Fiber.interrupt(exitFiber).pipe( @@ -30,7 +30,7 @@ const runPromise = (ctx?: Vitest.TaskContext) => (effect: Effect.Effect exit.value } else { From 52ff551015aa3eafa7748aa4030b1c8edd535570 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 27 Sep 2024 10:48:24 +1200 Subject: [PATCH 5/6] update docs --- .changeset/dull-laws-joke.md | 41 +++++++++++++++++++++++++++++- packages/vitest/src/index.ts | 39 ++++++++++++++++++++++++++++ packages/vitest/test/index.test.ts | 8 ++---- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/.changeset/dull-laws-joke.md b/.changeset/dull-laws-joke.md index cb3701011d..25aa81ed49 100644 --- a/.changeset/dull-laws-joke.md +++ b/.changeset/dull-laws-joke.md @@ -2,4 +2,43 @@ "@effect/vitest": patch --- -add vitest layer api +add layer api to `@effect/vitest` + +This allows you to share a `Layer` between multiple tests, optionally wrapping +the tests in a `describe` block. + +```ts +import { expect, layer } from "@effect/vitest" +import { Context, Effect, Layer } from "effect" + +class Foo extends Context.Tag("Foo")() { + static Live = Layer.succeed(Foo, "foo") +} + +class Bar extends Context.Tag("Bar")() { + static Live = Layer.effect( + Bar, + Effect.map(Foo, () => "bar" as const) + ) +} + +layer(Foo.Live)("layer", (it) => { + it.effect("adds context", () => + Effect.gen(function* () { + const foo = yield* Foo + expect(foo).toEqual("foo") + }) + ) + + it.layer(Bar.Live)("nested", (it) => { + it.effect("adds context", () => + Effect.gen(function* () { + const foo = yield* Foo + const bar = yield* Bar + expect(foo).toEqual("foo") + expect(bar).toEqual("bar") + }) + ) + }) +}) +``` diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index 09e1db8c9e..b03f57e4db 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -94,7 +94,46 @@ export const live: Vitest.Tester = internal.live export const scopedLive: Vitest.Tester = internal.scopedLive /** + * Share a `Layer` between multiple tests, optionally wrapping + * the tests in a `describe` block if a name is provided. + * * @since 1.0.0 + * + * ```ts + * import { expect, layer } from "@effect/vitest" + * import { Context, Effect, Layer } from "effect" + * + * class Foo extends Context.Tag("Foo")() { + * static Live = Layer.succeed(Foo, "foo") + * } + * + * class Bar extends Context.Tag("Bar")() { + * static Live = Layer.effect( + * Bar, + * Effect.map(Foo, () => "bar" as const) + * ) + * } + * + * layer(Foo.Live)("layer", (it) => { + * it.effect("adds context", () => + * Effect.gen(function* () { + * const foo = yield* Foo + * expect(foo).toEqual("foo") + * }) + * ) + * + * it.layer(Bar.Live)("nested", (it) => { + * it.effect("adds context", () => + * Effect.gen(function* () { + * const foo = yield* Foo + * const bar = yield* Bar + * expect(foo).toEqual("foo") + * expect(bar).toEqual("bar") + * }) + * ) + * }) + * }) + * ``` */ export const layer: ( layer_: Layer.Layer, diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 75eda0ce0a..4670862365 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -1,6 +1,5 @@ -import { afterAll, expect, it, layer } from "@effect/vitest" +import { afterAll, describe, expect, it, layer } from "@effect/vitest" import { Context, Effect, Layer } from "effect" -import { describe } from "node:test" it.live( "live %s", @@ -92,10 +91,7 @@ class Foo extends Context.Tag("Foo")() { } class Bar extends Context.Tag("Bar")() { - static Live = Layer.effect( - Bar, - Effect.map(Foo, () => "bar" as const) - ) + static Live = Layer.effect(Bar, Effect.map(Foo, () => "bar" as const)) } layer(Foo.Live)("layer", (it) => { From aa7d145575c9bbcf817a1eed7d092e9063f5c9fc Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 27 Sep 2024 10:58:57 +1200 Subject: [PATCH 6/6] defer resource creation to suite creation --- packages/vitest/src/internal.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index 02c91ba808..70182350c4 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -103,7 +103,10 @@ const makeTester = ( export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap): { (f: (it: Vitest.Vitest.Methods) => void): void (name: string, f: (it: Vitest.Vitest.Methods) => void): void -} => { +} => +( + ...args: [name: string, f: (it: Vitest.Vitest.Methods) => void] | [f: (it: Vitest.Vitest.Methods) => void] +) => { memoMap = memoMap ?? Effect.runSync(Layer.makeMemoMap) const scope = Effect.runSync(Scope.make()) const runtimeEffect = Layer.toRuntimeWithMemoMap(layer_, memoMap).pipe( @@ -112,8 +115,6 @@ export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap): Effect.cached, Effect.runSync ) - V.beforeAll(() => runPromise()(Effect.asVoid(runtimeEffect))) - V.afterAll(() => runPromise()(Scope.close(scope, Exit.void))) const it: Vitest.Vitest.Methods = Object.assign(V.it, { effect: makeTester((effect) => @@ -150,14 +151,17 @@ export const layer = (layer_: Layer.Layer, memoMap?: Layer.MemoMap): } }) - return function( - ...args: [name: string, f: (it: Vitest.Vitest.Methods) => void] | [f: (it: Vitest.Vitest.Methods) => void] - ) { - if (args.length === 1) { - return args[0](it) - } - return V.describe(args[0], () => args[1](it)) + if (args.length === 1) { + V.beforeAll(() => runPromise()(Effect.asVoid(runtimeEffect))) + V.afterAll(() => runPromise()(Scope.close(scope, Exit.void))) + return args[0](it) } + + return V.describe(args[0], () => { + V.beforeAll(() => runPromise()(Effect.asVoid(runtimeEffect))) + V.afterAll(() => runPromise()(Scope.close(scope, Exit.void))) + return args[1](it) + }) } /** @internal */