@shivaedev/effect-test 0.1.1 → 0.1.3

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 CHANGED
@@ -1,6 +1,35 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.1.3 - 2026-10-04
4
+
5
+ ### Changed
6
+
7
+ - Imports that leave their folder go through `#` aliases declared in the `imports` field of `package.json`: `#*.ts` resolves to `src` under the `source` condition and to `dist` otherwise, and `#test/*` names a test file. The published modules and declarations name the package's own files through these aliases.
8
+
9
+ - Consolidate duplicate test cases around observable package behavior.
10
+
11
+ - Validate packed consumers and executable bins through the shared workspace gate.
12
+
13
+ - Build with the `@shivaedev/quality` tsconfig presets, which target ESNext and allow only erasable TypeScript syntax.
14
+
15
+ - Type-check with TypeScript 7 only. TypeScript 6 still builds the package.
16
+
17
+ - Lint and format with the `@shivaedev/quality` Biome preset through `quality lint` at the repository root, which replaces the package's `check` script.
18
+
19
+ - Run tests through the `@shivaedev/quality` Vitest projects, which take a test's environment from its file name. Type tests are named `*.typecheck.test.ts`.
20
+
21
+ - Keep `package.json` in sort-package-json key order, checked by `quality lint`.
22
+
23
+ ## 0.1.2 - 2026-09-26
24
+
25
+ ### Fixed
26
+
27
+ - Make `eventually` retry only typed failures under TestClock, matching the live
28
+ clock. Defects and interruption propagate without retrying.
29
+
30
+ ### Changed
31
+
32
+ - Document the live worker-Layer clock and the scope of per-test clock overrides.
4
33
 
5
34
  ## 0.1.1 - 2026-09-04
6
35
 
package/README.md CHANGED
@@ -58,10 +58,20 @@ effectApp("talks to a real timer", function* () {
58
58
  }, { clock: "live" })
59
59
  ```
60
60
 
61
+ The worker Layer is acquired and released outside the per-test environment,
62
+ using the live clock unless the Layer supplies its own. Fibers started during
63
+ Layer acquisition keep that environment; advancing a test's TestClock does not
64
+ advance their timers. The selected per-test clock applies to `around`,
65
+ `makeHarness`, and the test body.
66
+
61
67
  ## `eventually`
62
68
 
63
- Retries until the effect succeeds. Under TestClock it advances time with
64
- `TestClock.adjust`. Under `clock: "live"` it uses `Schedule.spaced`.
69
+ Retries typed failures until the effect succeeds. Under TestClock it advances
70
+ time with `TestClock.adjust`. Under `clock: "live"` it uses `Schedule.spaced`.
71
+ `times` counts retries after the initial attempt; omitting it retries without a
72
+ limit. Defects (including thrown assertions) and interruption propagate without
73
+ retrying under either clock. Use `Effect.try(() => expect(...))` when an
74
+ assertion failure is intentionally a retryable condition.
65
75
 
66
76
  ```ts
67
77
  import { eventually } from "@shivaedev/effect-test"
@@ -0,0 +1,3 @@
1
+ import type { Layer } from "effect";
2
+ export type AnyTestLayer = Layer.Layer<any, any, never>;
3
+ //# sourceMappingURL=any-test-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"any-test-layer.d.ts","sourceRoot":"","sources":["../src/any-test-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEpC,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=any-test-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"any-test-layer.js","sourceRoot":"","sources":["../src/any-test-layer.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"eventually.d.ts","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAGtE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAOD,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACjC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,UAAU,iBAAiB,KACzB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CA4BrB,CAAC"}
1
+ {"version":3,"file":"eventually.d.ts","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAGxE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAgCD,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,iBAAiB,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAMpH,CAAC"}
@@ -1,26 +1,22 @@
1
- import { Clock, Effect, Exit, Schedule } from "effect";
1
+ import { Clock, Effect, Result, Schedule } from "effect";
2
2
  import * as TestClock from "effect/testing/TestClock";
3
3
  const defaultInterval = "10 millis";
4
4
  const isTestClock = (clock) => "adjust" in clock;
5
- export const eventually = (effect, options) => Effect.gen(function* () {
6
- const interval = options?.interval ?? defaultInterval;
7
- const clock = yield* Clock.Clock;
8
- if (!isTestClock(clock)) {
9
- return yield* options?.times === undefined
10
- ? Effect.retry(effect, Schedule.spaced(interval))
11
- : Effect.retry(effect, {
12
- schedule: Schedule.spaced(interval),
13
- times: options.times,
14
- });
15
- }
16
- let retriesLeft = options?.times;
5
+ const retryOnLiveClock = (effect, interval, times) => times === undefined
6
+ ? Effect.retry(effect, Schedule.spaced(interval))
7
+ : Effect.retry(effect, {
8
+ schedule: Schedule.spaced(interval),
9
+ times,
10
+ });
11
+ const retryOnTestClock = (effect, interval, times) => Effect.gen(function* () {
12
+ let retriesLeft = times;
17
13
  for (;;) {
18
- const exit = yield* Effect.exit(effect);
19
- if (Exit.isSuccess(exit)) {
20
- return exit.value;
14
+ const result = yield* Effect.result(effect);
15
+ if (Result.isSuccess(result)) {
16
+ return result.success;
21
17
  }
22
18
  if (retriesLeft === 0) {
23
- return yield* exit;
19
+ return yield* Effect.fail(result.failure);
24
20
  }
25
21
  if (retriesLeft !== undefined) {
26
22
  retriesLeft -= 1;
@@ -28,4 +24,10 @@ export const eventually = (effect, options) => Effect.gen(function* () {
28
24
  yield* TestClock.adjust(interval);
29
25
  }
30
26
  });
27
+ export const eventually = (effect, options) => Effect.gen(function* () {
28
+ const interval = options?.interval ?? defaultInterval;
29
+ const clock = yield* Clock.Clock;
30
+ const retry = isTestClock(clock) ? retryOnTestClock : retryOnLiveClock;
31
+ return yield* retry(effect, interval, options?.times);
32
+ });
31
33
  //# sourceMappingURL=eventually.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"eventually.js","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAiB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AAOtD,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,CAAC,KAAkB,EAAgC,EAAE,CACxE,QAAQ,IAAI,KAAK,CAAC;AAEnB,MAAM,CAAC,MAAM,UAAU,GAAG,CACzB,MAA8B,EAC9B,OAA2B,EACF,EAAE,CAC3B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,eAAe,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAEjC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS;YACzC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACjD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;gBACrB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;gBACnC,KAAK,EAAE,OAAO,CAAC,KAAK;aACpB,CAAC,CAAC;IACN,CAAC;IAED,IAAI,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC;IACjC,SAAS,CAAC;QACT,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,KAAK,CAAC;QACnB,CAAC;QACD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC/B,WAAW,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;AACF,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"eventually.js","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAiB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACxE,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AAOtD,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,CAAC,KAAkB,EAAgC,EAAE,CAAC,QAAQ,IAAI,KAAK,CAAC;AAE5F,MAAM,gBAAgB,GAAG,CAAU,MAA8B,EAAE,QAAwB,EAAE,KAAyB,EAA0B,EAAE,CACjJ,KAAK,KAAK,SAAS;IAClB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;QACrB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnC,KAAK;KACL,CAAC,CAAC;AAEN,MAAM,gBAAgB,GAAG,CAAU,MAA8B,EAAE,QAAwB,EAAE,KAAyB,EAA0B,EAAE,CACjJ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,SAAS,CAAC;QACT,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAC,OAAO,CAAC;QACvB,CAAC;QACD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC/B,WAAW,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;AACF,CAAC,CAAC,CAAC;AAEJ,MAAM,CAAC,MAAM,UAAU,GAAG,CAAU,MAA8B,EAAE,OAA2B,EAA0B,EAAE,CAC1H,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,eAAe,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACvE,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type EventuallyOptions, eventually } from "./eventually.js";
2
- export type { EffectClock, EffectTest, EffectTester, EffectTestOptions, MakeEffectItOptions, MakeEffectItResult, } from "./types.js";
3
- export { makeEffectIt } from "./vitest.js";
1
+ export { type EventuallyOptions, eventually } from "./eventually.ts";
2
+ export type { EffectClock, EffectTest, EffectTester, EffectTestOptions, MakeEffectItOptions, MakeEffectItResult, } from "./types.ts";
3
+ export { makeEffectIt } from "./vitest.ts";
4
4
  //# sourceMappingURL=index.d.ts.map
package/dist/types.d.ts CHANGED
@@ -1,23 +1,24 @@
1
1
  import type { TestContext, TestOptions } from "@effect/vitest";
2
2
  import type { Effect, Layer } from "effect";
3
+ import type { AnyTestLayer } from "./any-test-layer.ts";
3
4
  export type EffectClock = "test" | "live";
4
5
  export type EffectTestOptions = TestOptions & {
5
6
  readonly clock?: EffectClock;
6
7
  };
7
8
  export type EffectTest<Harness, Provided> = <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(name: string, body: (harness: Harness, context: TestContext) => Generator<Eff, A, never>, options?: number | EffectTestOptions) => void;
8
9
  export interface EffectTester<Harness, Provided> extends EffectTest<Harness, Provided> {
10
+ readonly each: <Item>(cases: readonly Item[]) => <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(name: string, body: (item: Item, harness: Harness, context: TestContext) => Generator<Eff, A, never>, options?: number | EffectTestOptions) => void;
11
+ readonly fails: EffectTest<Harness, Provided>;
12
+ readonly only: EffectTest<Harness, Provided>;
13
+ readonly runIf: (condition: unknown) => EffectTest<Harness, Provided>;
9
14
  readonly skip: EffectTest<Harness, Provided>;
10
15
  readonly skipIf: (condition: unknown) => EffectTest<Harness, Provided>;
11
- readonly runIf: (condition: unknown) => EffectTest<Harness, Provided>;
12
- readonly only: EffectTest<Harness, Provided>;
13
- readonly each: <Item>(cases: ReadonlyArray<Item>) => <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(name: string, body: (item: Item, harness: Harness, context: TestContext) => Generator<Eff, A, never>, options?: number | EffectTestOptions) => void;
14
- readonly fails: EffectTest<Harness, Provided>;
15
16
  }
16
- export interface MakeEffectItOptions<Harness, TestLayer extends Layer.Layer<any, any, never>> {
17
+ export interface MakeEffectItOptions<Harness, TestLayer extends AnyTestLayer> {
18
+ readonly around?: (<A, E>(effect: Effect.Effect<A, E, Layer.Success<TestLayer>>) => Effect.Effect<A, unknown, Layer.Success<TestLayer>>) | undefined;
19
+ readonly clock?: EffectClock | undefined;
17
20
  readonly layer: TestLayer;
18
- readonly around?: <A, E>(effect: Effect.Effect<A, E, Layer.Success<TestLayer>>) => Effect.Effect<A, unknown, Layer.Success<TestLayer>>;
19
21
  readonly makeHarness: (context: TestContext) => Effect.Effect<Harness, unknown, Layer.Success<TestLayer>>;
20
- readonly clock?: EffectClock;
21
22
  }
22
23
  export interface MakeEffectItResult<Harness, Provided> {
23
24
  readonly effectApp: EffectTester<Harness, Provided>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1C,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,IAAI,CAC3C,CAAC,EACD,GAAG,SAAS,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAErD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,KAAK,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,EAC1E,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,KAChC,IAAI,CAAC;AAEV,MAAM,WAAW,YAAY,CAAC,OAAO,EAAE,QAAQ,CAC9C,SAAQ,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvE,QAAQ,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EACnB,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KACtB,CAAC,CAAC,EAAE,GAAG,SAAS,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAC7D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CACL,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,KAChB,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,EAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,KAChC,IAAI,CAAC;IACV,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB,CACnC,OAAO,EAEP,SAAS,SAAS,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC;IAE9C,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KACjD,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,WAAW,EAAE,CACrB,OAAO,EAAE,WAAW,KAChB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB,CAAC,OAAO,EAAE,QAAQ;IACpD,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;CACpD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/vitest.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Layer } from "effect";
2
- import type { MakeEffectItOptions, MakeEffectItResult } from "./types.js";
3
- export declare const makeEffectIt: <Harness, TestLayer extends Layer.Layer<any, any, never>>(options: MakeEffectItOptions<Harness, TestLayer>) => MakeEffectItResult<Harness, Layer.Success<TestLayer>>;
2
+ import type { AnyTestLayer } from "./any-test-layer.ts";
3
+ import type { MakeEffectItOptions, MakeEffectItResult } from "./types.ts";
4
+ export declare const makeEffectIt: <Harness, TestLayer extends AnyTestLayer>(options: MakeEffectItOptions<Harness, TestLayer>) => MakeEffectItResult<Harness, Layer.Success<TestLayer>>;
4
5
  //# sourceMappingURL=vitest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAKA,OAAO,EAAqC,KAAK,EAAS,MAAM,QAAQ,CAAC;AAGzE,OAAO,KAAK,EAKX,mBAAmB,EACnB,kBAAkB,EAClB,MAAM,YAAY,CAAC;AAyGpB,eAAO,MAAM,YAAY,GACxB,OAAO,EAEP,SAAS,SAAS,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAE9C,SAAS,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,KAC9C,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAuHtD,CAAC"}
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EAAqC,KAAK,EAAS,MAAM,QAAQ,CAAC;AAGzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAA4D,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkEpI,eAAO,MAAM,YAAY,GAAI,OAAO,EAAE,SAAS,SAAS,YAAY,EACnE,SAAS,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,KAC9C,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAyEtD,CAAC"}
package/dist/vitest.js CHANGED
@@ -1,16 +1,15 @@
1
- import { it as effectIt, } from "@effect/vitest";
1
+ import { it as effectIt } from "@effect/vitest";
2
2
  import { Cause, Effect, Exit, Layer, Scope } from "effect";
3
3
  import * as TestClock from "effect/testing/TestClock";
4
4
  import * as TestConsole from "effect/testing/TestConsole";
5
5
  const fixtureName = "__effectTestContext";
6
6
  const TestClockEnvironment = Layer.mergeAll(TestConsole.layer, TestClock.layer());
7
7
  const LiveClockEnvironment = TestConsole.layer;
8
- const environmentFor = (clock) => clock === "live" ? LiveClockEnvironment : TestClockEnvironment;
8
+ const environmentFor = (clock) => (clock === "live" ? LiveClockEnvironment : TestClockEnvironment);
9
9
  const restoreContext = (fixture, context) => ({
10
10
  ...context,
11
11
  [fixtureName]: fixture,
12
12
  });
13
- const contextFrom = (context) => context[fixtureName];
14
13
  const runEffectTest = (effect, context, clock) => Effect.runPromise(Effect.gen(function* () {
15
14
  const exit = yield* Effect.exit(effect);
16
15
  if (Exit.isFailure(exit)) {
@@ -30,9 +29,7 @@ const splitOptions = (options) => {
30
29
  const { clock, ...vitest } = options;
31
30
  return { clock, vitest };
32
31
  };
33
- const makeFixtureIt = (layer) => effectIt.extend(fixtureName, { scope: "worker" },
34
- // biome-ignore lint/correctness/noEmptyPattern: Vitest fixtures require a destructured context parameter.
35
- async ({}, { onCleanup }) => {
32
+ const makeFixtureIt = (layer) => effectIt.extend(fixtureName, { scope: "worker" }, async ({}, { onCleanup }) => {
36
33
  const scope = Effect.runSync(Scope.make());
37
34
  onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
38
35
  try {
@@ -47,21 +44,19 @@ export const makeEffectIt = (options) => {
47
44
  const fixtureIt = makeFixtureIt(options.layer);
48
45
  const defaultClock = options.clock ?? "test";
49
46
  const run = (body, context) => {
50
- const program = Effect.gen(function* () {
47
+ const ready = Effect.gen(function* () {
51
48
  const harness = yield* options.makeHarness(context);
52
49
  return yield* Effect.gen(() => body(harness, context));
53
50
  });
54
- // The body constraint proves every yielded service is part of Provided.
55
- // TypeScript cannot reduce that generic generator requirement here.
56
- const ready = program;
57
51
  const wrapped = options.around?.(ready) ?? ready;
58
- return wrapped.pipe(Effect.provide(contextFrom(context)));
52
+ return wrapped.pipe(Effect.provide(context[fixtureName]));
59
53
  };
60
54
  const register = (current) => (name, body, testOptions) => {
61
55
  const split = splitOptions(testOptions);
62
56
  const clock = split.clock ?? defaultClock;
63
- current(name, split.vitest, ({ __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local, }) => {
57
+ current(name, split.vitest, ({ __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local }) => {
64
58
  const context = restoreContext(__effectTestContext, {
59
+ _local,
65
60
  annotate,
66
61
  expect,
67
62
  onTestFailed,
@@ -69,21 +64,17 @@ export const makeEffectIt = (options) => {
69
64
  signal,
70
65
  skip,
71
66
  task,
72
- _local,
73
67
  });
74
68
  return runEffectTest(run(body, context), context, clock);
75
69
  });
76
70
  };
77
71
  const effectApp = Object.assign(register(fixtureIt), {
78
- skip: register(fixtureIt.skip),
79
- skipIf: (condition) => register(fixtureIt.skipIf(condition)),
80
- runIf: (condition) => register(fixtureIt.runIf(condition)),
81
- only: register(fixtureIt.only),
82
72
  each: (cases) => (name, body, testOptions) => {
83
73
  const split = splitOptions(testOptions);
84
74
  const clock = split.clock ?? defaultClock;
85
- fixtureIt.for(cases)(name, split.vitest, (item, { __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local, }) => {
75
+ fixtureIt.for(cases)(name, split.vitest, (item, { __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local }) => {
86
76
  const context = restoreContext(__effectTestContext, {
77
+ _local,
87
78
  annotate,
88
79
  expect,
89
80
  onTestFailed,
@@ -91,12 +82,15 @@ export const makeEffectIt = (options) => {
91
82
  signal,
92
83
  skip,
93
84
  task,
94
- _local,
95
85
  });
96
- return runEffectTest(run((harness, testContext) => body(item, harness, testContext), context), context, clock);
86
+ return runEffectTest(Effect.asVoid(run((harness, testContext) => body(item, harness, testContext), context)), context, clock);
97
87
  });
98
88
  },
99
89
  fails: register(fixtureIt.fails),
90
+ only: register(fixtureIt.only),
91
+ runIf: (condition) => register(fixtureIt.runIf(condition)),
92
+ skip: register(fixtureIt.skip),
93
+ skipIf: (condition) => register(fixtureIt.skipIf(condition)),
100
94
  });
101
95
  return { effectApp };
102
96
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,28 +1,27 @@
1
1
  {
2
2
  "name": "@shivaedev/effect-test",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Generic Effect Vitest runner with worker-scoped Layers and TestClock",
5
- "type": "module",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/ShivaeDev/platform.git",
10
- "directory": "packages/effect-test"
11
- },
12
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-test#readme",
13
6
  "bugs": {
14
7
  "url": "https://github.com/ShivaeDev/platform/issues"
15
8
  },
16
- "engines": {
17
- "node": ">=24"
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/ShivaeDev/platform.git",
12
+ "directory": "packages/effect-test"
18
13
  },
14
+ "license": "MIT",
19
15
  "sideEffects": false,
20
- "files": [
21
- "dist",
22
- "src",
23
- "CHANGELOG.md",
24
- "README.md"
25
- ],
16
+ "type": "module",
17
+ "imports": {
18
+ "#test/*": "./test/*",
19
+ "#*.ts": {
20
+ "source": "./src/*.ts",
21
+ "types": "./dist/*.d.ts",
22
+ "default": "./dist/*.js"
23
+ }
24
+ },
26
25
  "exports": {
27
26
  ".": {
28
27
  "types": "./dist/index.d.ts",
@@ -32,9 +31,20 @@
32
31
  },
33
32
  "./package.json": "./package.json"
34
33
  },
35
- "publishConfig": {
36
- "access": "public",
37
- "provenance": true
34
+ "files": [
35
+ "dist",
36
+ "src",
37
+ "CHANGELOG.md",
38
+ "README.md"
39
+ ],
40
+ "devDependencies": {
41
+ "@effect/vitest": "4.0.0-rc.112",
42
+ "@shivaedev/quality": "0.7.3",
43
+ "@types/node": "24.10.1",
44
+ "@typescript/native": "npm:typescript@7.0.2",
45
+ "effect": "4.0.0-rc.112",
46
+ "typescript": "npm:@typescript/typescript6@6.0.2",
47
+ "vitest": "4.1.9"
38
48
  },
39
49
  "peerDependencies": {
40
50
  "@effect/vitest": "4.0.0-rc.112",
@@ -45,21 +55,17 @@
45
55
  "optional": true
46
56
  }
47
57
  },
48
- "devDependencies": {
49
- "@effect/vitest": "4.0.0-rc.112",
50
- "@types/node": "24.10.1",
51
- "@typescript/native": "npm:typescript@7.0.2",
52
- "effect": "4.0.0-rc.112",
53
- "typescript": "npm:@typescript/typescript6@6.0.2",
54
- "vitest": "4.1.9"
58
+ "engines": {
59
+ "node": ">=24"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public",
63
+ "provenance": true
55
64
  },
56
65
  "scripts": {
57
66
  "build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc6 --project tsconfig.build.json",
58
- "check": "biome check .",
67
+ "ready": "pnpm typecheck && pnpm test && pnpm build",
59
68
  "test": "vitest run",
60
- "test:package": "node scripts/test-package.mjs",
61
- "typecheck": "tsc --noEmit",
62
- "typecheck:compat": "tsc6 --noEmit",
63
- "ready": "pnpm check && pnpm typecheck && pnpm typecheck:compat && pnpm test && pnpm build && pnpm test:package"
69
+ "typecheck": "tsc --noEmit"
64
70
  }
65
71
  }
@@ -0,0 +1,3 @@
1
+ import type { Layer } from "effect";
2
+
3
+ export type AnyTestLayer = Layer.Layer<any, any, never>;
package/src/eventually.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Clock, type Duration, Effect, Exit, Schedule } from "effect";
1
+ import { Clock, type Duration, Effect, Result, Schedule } from "effect";
2
2
  import * as TestClock from "effect/testing/TestClock";
3
3
 
4
4
  export interface EventuallyOptions {
@@ -8,34 +8,26 @@ export interface EventuallyOptions {
8
8
 
9
9
  const defaultInterval = "10 millis";
10
10
 
11
- const isTestClock = (clock: Clock.Clock): clock is TestClock.TestClock =>
12
- "adjust" in clock;
11
+ const isTestClock = (clock: Clock.Clock): clock is TestClock.TestClock => "adjust" in clock;
13
12
 
14
- export const eventually = <A, E, R>(
15
- effect: Effect.Effect<A, E, R>,
16
- options?: EventuallyOptions,
17
- ): Effect.Effect<A, E, R> =>
18
- Effect.gen(function* () {
19
- const interval = options?.interval ?? defaultInterval;
20
- const clock = yield* Clock.Clock;
13
+ const retryOnLiveClock = <A, E, R>(effect: Effect.Effect<A, E, R>, interval: Duration.Input, times: number | undefined): Effect.Effect<A, E, R> =>
14
+ times === undefined
15
+ ? Effect.retry(effect, Schedule.spaced(interval))
16
+ : Effect.retry(effect, {
17
+ schedule: Schedule.spaced(interval),
18
+ times,
19
+ });
21
20
 
22
- if (!isTestClock(clock)) {
23
- return yield* options?.times === undefined
24
- ? Effect.retry(effect, Schedule.spaced(interval))
25
- : Effect.retry(effect, {
26
- schedule: Schedule.spaced(interval),
27
- times: options.times,
28
- });
29
- }
30
-
31
- let retriesLeft = options?.times;
21
+ const retryOnTestClock = <A, E, R>(effect: Effect.Effect<A, E, R>, interval: Duration.Input, times: number | undefined): Effect.Effect<A, E, R> =>
22
+ Effect.gen(function* () {
23
+ let retriesLeft = times;
32
24
  for (;;) {
33
- const exit = yield* Effect.exit(effect);
34
- if (Exit.isSuccess(exit)) {
35
- return exit.value;
25
+ const result = yield* Effect.result(effect);
26
+ if (Result.isSuccess(result)) {
27
+ return result.success;
36
28
  }
37
29
  if (retriesLeft === 0) {
38
- return yield* exit;
30
+ return yield* Effect.fail(result.failure);
39
31
  }
40
32
  if (retriesLeft !== undefined) {
41
33
  retriesLeft -= 1;
@@ -43,3 +35,11 @@ export const eventually = <A, E, R>(
43
35
  yield* TestClock.adjust(interval);
44
36
  }
45
37
  });
38
+
39
+ export const eventually = <A, E, R>(effect: Effect.Effect<A, E, R>, options?: EventuallyOptions): Effect.Effect<A, E, R> =>
40
+ Effect.gen(function* () {
41
+ const interval = options?.interval ?? defaultInterval;
42
+ const clock = yield* Clock.Clock;
43
+ const retry = isTestClock(clock) ? retryOnTestClock : retryOnLiveClock;
44
+ return yield* retry(effect, interval, options?.times);
45
+ });
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type EventuallyOptions, eventually } from "./eventually.js";
1
+ export { type EventuallyOptions, eventually } from "./eventually.ts";
2
2
  export type {
3
3
  EffectClock,
4
4
  EffectTest,
@@ -6,5 +6,5 @@ export type {
6
6
  EffectTestOptions,
7
7
  MakeEffectItOptions,
8
8
  MakeEffectItResult,
9
- } from "./types.js";
10
- export { makeEffectIt } from "./vitest.js";
9
+ } from "./types.ts";
10
+ export { makeEffectIt } from "./vitest.ts";
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { TestContext, TestOptions } from "@effect/vitest";
2
2
  import type { Effect, Layer } from "effect";
3
+ import type { AnyTestLayer } from "./any-test-layer.ts";
3
4
 
4
5
  export type EffectClock = "test" | "live";
5
6
 
@@ -7,48 +8,34 @@ export type EffectTestOptions = TestOptions & {
7
8
  readonly clock?: EffectClock;
8
9
  };
9
10
 
10
- export type EffectTest<Harness, Provided> = <
11
- A,
12
- Eff extends Effect.Effect<unknown, unknown, Provided>,
13
- >(
11
+ export type EffectTest<Harness, Provided> = <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
14
12
  name: string,
15
13
  body: (harness: Harness, context: TestContext) => Generator<Eff, A, never>,
16
14
  options?: number | EffectTestOptions,
17
15
  ) => void;
18
16
 
19
- export interface EffectTester<Harness, Provided>
20
- extends EffectTest<Harness, Provided> {
21
- readonly skip: EffectTest<Harness, Provided>;
22
- readonly skipIf: (condition: unknown) => EffectTest<Harness, Provided>;
23
- readonly runIf: (condition: unknown) => EffectTest<Harness, Provided>;
24
- readonly only: EffectTest<Harness, Provided>;
17
+ export interface EffectTester<Harness, Provided> extends EffectTest<Harness, Provided> {
25
18
  readonly each: <Item>(
26
- cases: ReadonlyArray<Item>,
19
+ cases: readonly Item[],
27
20
  ) => <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
28
21
  name: string,
29
- body: (
30
- item: Item,
31
- harness: Harness,
32
- context: TestContext,
33
- ) => Generator<Eff, A, never>,
22
+ body: (item: Item, harness: Harness, context: TestContext) => Generator<Eff, A, never>,
34
23
  options?: number | EffectTestOptions,
35
24
  ) => void;
36
25
  readonly fails: EffectTest<Harness, Provided>;
26
+ readonly only: EffectTest<Harness, Provided>;
27
+ readonly runIf: (condition: unknown) => EffectTest<Harness, Provided>;
28
+ readonly skip: EffectTest<Harness, Provided>;
29
+ readonly skipIf: (condition: unknown) => EffectTest<Harness, Provided>;
37
30
  }
38
31
 
39
- export interface MakeEffectItOptions<
40
- Harness,
41
- // biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
42
- TestLayer extends Layer.Layer<any, any, never>,
43
- > {
32
+ export interface MakeEffectItOptions<Harness, TestLayer extends AnyTestLayer> {
33
+ readonly around?:
34
+ | (<A, E>(effect: Effect.Effect<A, E, Layer.Success<TestLayer>>) => Effect.Effect<A, unknown, Layer.Success<TestLayer>>)
35
+ | undefined;
36
+ readonly clock?: EffectClock | undefined;
44
37
  readonly layer: TestLayer;
45
- readonly around?: <A, E>(
46
- effect: Effect.Effect<A, E, Layer.Success<TestLayer>>,
47
- ) => Effect.Effect<A, unknown, Layer.Success<TestLayer>>;
48
- readonly makeHarness: (
49
- context: TestContext,
50
- ) => Effect.Effect<Harness, unknown, Layer.Success<TestLayer>>;
51
- readonly clock?: EffectClock;
38
+ readonly makeHarness: (context: TestContext) => Effect.Effect<Harness, unknown, Layer.Success<TestLayer>>;
52
39
  }
53
40
 
54
41
  export interface MakeEffectItResult<Harness, Provided> {
package/src/vitest.ts CHANGED
@@ -1,19 +1,9 @@
1
- import {
2
- it as effectIt,
3
- type TestContext,
4
- type TestOptions,
5
- } from "@effect/vitest";
1
+ import { it as effectIt, type TestContext, type TestOptions } from "@effect/vitest";
6
2
  import { Cause, type Context, Effect, Exit, Layer, Scope } from "effect";
7
3
  import * as TestClock from "effect/testing/TestClock";
8
4
  import * as TestConsole from "effect/testing/TestConsole";
9
- import type {
10
- EffectClock,
11
- EffectTest,
12
- EffectTester,
13
- EffectTestOptions,
14
- MakeEffectItOptions,
15
- MakeEffectItResult,
16
- } from "./types.js";
5
+ import type { AnyTestLayer } from "./any-test-layer.ts";
6
+ import type { EffectClock, EffectTest, EffectTester, EffectTestOptions, MakeEffectItOptions, MakeEffectItResult } from "./types.ts";
17
7
 
18
8
  const fixtureName = "__effectTestContext";
19
9
 
@@ -21,54 +11,22 @@ type EffectFixture<Provided> = TestContext & {
21
11
  readonly [fixtureName]: Context.Context<Provided>;
22
12
  };
23
13
 
24
- interface FixtureTestApi<Provided> {
25
- (
26
- name: string,
27
- options: TestOptions,
28
- body: (context: EffectFixture<Provided>) => Promise<unknown>,
29
- ): void;
30
- readonly skip: FixtureTestApi<Provided>;
31
- readonly skipIf: (condition: unknown) => FixtureTestApi<Provided>;
32
- readonly runIf: (condition: unknown) => FixtureTestApi<Provided>;
33
- readonly only: FixtureTestApi<Provided>;
34
- readonly fails: FixtureTestApi<Provided>;
35
- readonly for: <Item>(
36
- cases: ReadonlyArray<Item>,
37
- ) => (
38
- name: string,
39
- options: TestOptions,
40
- body: (item: Item, context: EffectFixture<Provided>) => Promise<unknown>,
41
- ) => void;
42
- }
14
+ type FixtureTest<Provided> = (name: string, options: TestOptions, body: (context: EffectFixture<Provided>) => Promise<unknown>) => void;
43
15
 
44
- const TestClockEnvironment = Layer.mergeAll(
45
- TestConsole.layer,
46
- TestClock.layer(),
47
- );
16
+ const TestClockEnvironment = Layer.mergeAll(TestConsole.layer, TestClock.layer());
48
17
  const LiveClockEnvironment = TestConsole.layer;
49
18
 
50
- const environmentFor = (clock: EffectClock) =>
51
- clock === "live" ? LiveClockEnvironment : TestClockEnvironment;
19
+ const environmentFor = (clock: EffectClock) => (clock === "live" ? LiveClockEnvironment : TestClockEnvironment);
52
20
 
53
21
  const restoreContext = <Provided>(
54
22
  fixture: Context.Context<Provided>,
55
23
  context: Omit<EffectFixture<Provided>, typeof fixtureName>,
56
- ): EffectFixture<Provided> =>
57
- ({
58
- ...context,
59
- [fixtureName]: fixture,
60
- }) as EffectFixture<Provided>;
24
+ ): EffectFixture<Provided> => ({
25
+ ...context,
26
+ [fixtureName]: fixture,
27
+ });
61
28
 
62
- const contextFrom = <Provided>(
63
- context: TestContext,
64
- ): Context.Context<Provided> =>
65
- (context as EffectFixture<Provided>)[fixtureName];
66
-
67
- const runEffectTest = <A, E>(
68
- effect: Effect.Effect<A, E, Scope.Scope>,
69
- context: TestContext,
70
- clock: EffectClock,
71
- ): Promise<A> =>
29
+ const runEffectTest = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>, context: TestContext, clock: EffectClock): Promise<A> =>
72
30
  Effect.runPromise(
73
31
  Effect.gen(function* () {
74
32
  const exit = yield* Effect.exit(effect);
@@ -98,149 +56,92 @@ const splitOptions = (
98
56
  return { clock, vitest };
99
57
  };
100
58
 
101
- const makeFixtureIt = <Provided, LayerError>(
102
- layer: Layer.Layer<Provided, LayerError>,
103
- ): FixtureTestApi<Provided> =>
104
- effectIt.extend(
105
- fixtureName,
106
- { scope: "worker" },
107
- // biome-ignore lint/correctness/noEmptyPattern: Vitest fixtures require a destructured context parameter.
108
- async ({}, { onCleanup }) => {
109
- const scope = Effect.runSync(Scope.make());
110
- onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
59
+ const makeFixtureIt = <Provided, LayerError>(layer: Layer.Layer<Provided, LayerError>) =>
60
+ effectIt.extend(fixtureName, { scope: "worker" }, async ({}, { onCleanup }) => {
61
+ const scope = Effect.runSync(Scope.make());
62
+ onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
111
63
 
112
- try {
113
- return await Effect.runPromise(Layer.buildWithScope(layer, scope));
114
- } catch (error) {
115
- await Effect.runPromise(Scope.close(scope, Exit.void));
116
- throw error;
117
- }
118
- },
119
- ) as unknown as FixtureTestApi<Provided>;
64
+ try {
65
+ return await Effect.runPromise(Layer.buildWithScope(layer, scope));
66
+ } catch (error) {
67
+ await Effect.runPromise(Scope.close(scope, Exit.void));
68
+ throw error;
69
+ }
70
+ });
120
71
 
121
- export const makeEffectIt = <
122
- Harness,
123
- // biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
124
- TestLayer extends Layer.Layer<any, any, never>,
125
- >(
72
+ export const makeEffectIt = <Harness, TestLayer extends AnyTestLayer>(
126
73
  options: MakeEffectItOptions<Harness, TestLayer>,
127
74
  ): MakeEffectItResult<Harness, Layer.Success<TestLayer>> => {
128
75
  type Provided = Layer.Success<TestLayer>;
129
76
  const fixtureIt = makeFixtureIt(options.layer);
130
77
  const defaultClock = options.clock ?? "test";
131
78
 
132
- const run = <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
133
- body: (harness: Harness, context: TestContext) => Generator<Eff, A, never>,
134
- context: TestContext,
79
+ const run = <A>(
80
+ body: (harness: Harness, context: TestContext) => Generator<Effect.Effect<unknown, unknown, Provided>, A, never>,
81
+ context: EffectFixture<Provided>,
135
82
  ): Effect.Effect<A, unknown, Scope.Scope> => {
136
- const program = Effect.gen(function* () {
83
+ const ready: Effect.Effect<A, unknown, Provided> = Effect.gen(function* () {
137
84
  const harness = yield* options.makeHarness(context);
138
85
  return yield* Effect.gen(() => body(harness, context));
139
86
  });
140
- // The body constraint proves every yielded service is part of Provided.
141
- // TypeScript cannot reduce that generic generator requirement here.
142
- const ready = program as Effect.Effect<A, unknown, Provided>;
143
87
  const wrapped = options.around?.(ready) ?? ready;
144
- return wrapped.pipe(
145
- Effect.provide(contextFrom<Provided>(context)),
146
- ) as Effect.Effect<A, unknown, Scope.Scope>;
88
+ return wrapped.pipe(Effect.provide(context[fixtureName]));
147
89
  };
148
90
 
149
91
  const register =
150
- (current: FixtureTestApi<Provided>): EffectTest<Harness, Provided> =>
92
+ (current: FixtureTest<Provided>): EffectTest<Harness, Provided> =>
151
93
  (name, body, testOptions) => {
152
94
  const split = splitOptions(testOptions);
153
95
  const clock = split.clock ?? defaultClock;
154
- current(
155
- name,
156
- split.vitest,
157
- ({
158
- __effectTestContext,
159
- task,
160
- signal,
96
+ current(name, split.vitest, ({ __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local }) => {
97
+ const context = restoreContext(__effectTestContext, {
98
+ _local,
99
+ annotate,
100
+ expect,
161
101
  onTestFailed,
162
102
  onTestFinished,
103
+ signal,
163
104
  skip,
164
- annotate,
165
- expect,
166
- _local,
167
- }) => {
168
- const context = restoreContext(__effectTestContext, {
169
- annotate,
170
- expect,
171
- onTestFailed,
172
- onTestFinished,
173
- signal,
174
- skip,
175
- task,
176
- _local,
177
- });
178
- return runEffectTest(run(body, context), context, clock);
179
- },
180
- );
105
+ task,
106
+ });
107
+ return runEffectTest(run(body, context), context, clock);
108
+ });
181
109
  };
182
110
 
183
- const effectApp: EffectTester<Harness, Provided> = Object.assign(
184
- register(fixtureIt),
185
- {
186
- skip: register(fixtureIt.skip),
187
- skipIf: (condition: unknown) => register(fixtureIt.skipIf(condition)),
188
- runIf: (condition: unknown) => register(fixtureIt.runIf(condition)),
189
- only: register(fixtureIt.only),
190
- each:
191
- <Item>(cases: ReadonlyArray<Item>) =>
192
- <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
193
- name: string,
194
- body: (
195
- item: Item,
196
- harness: Harness,
197
- context: TestContext,
198
- ) => Generator<Eff, A, never>,
199
- testOptions?: number | EffectTestOptions,
200
- ) => {
201
- const split = splitOptions(testOptions);
202
- const clock = split.clock ?? defaultClock;
203
- fixtureIt.for(cases)(
204
- name,
205
- split.vitest,
206
- (
207
- item,
208
- {
209
- __effectTestContext,
210
- task,
211
- signal,
212
- onTestFailed,
213
- onTestFinished,
214
- skip,
215
- annotate,
216
- expect,
217
- _local,
218
- },
219
- ) => {
220
- const context = restoreContext(__effectTestContext, {
221
- annotate,
222
- expect,
223
- onTestFailed,
224
- onTestFinished,
225
- signal,
226
- skip,
227
- task,
228
- _local,
229
- });
230
- return runEffectTest(
231
- run(
232
- (harness, testContext) => body(item, harness, testContext),
233
- context,
234
- ),
235
- context,
236
- clock,
237
- );
238
- },
239
- );
240
- },
241
- fails: register(fixtureIt.fails),
242
- },
243
- );
111
+ const effectApp: EffectTester<Harness, Provided> = Object.assign(register(fixtureIt), {
112
+ each:
113
+ <Item>(cases: readonly Item[]) =>
114
+ <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
115
+ name: string,
116
+ body: (item: Item, harness: Harness, context: TestContext) => Generator<Eff, A, never>,
117
+ testOptions?: number | EffectTestOptions,
118
+ ) => {
119
+ const split = splitOptions(testOptions);
120
+ const clock = split.clock ?? defaultClock;
121
+ fixtureIt.for(cases)(
122
+ name,
123
+ split.vitest,
124
+ (item, { __effectTestContext, task, signal, onTestFailed, onTestFinished, skip, annotate, expect, _local }) => {
125
+ const context = restoreContext(__effectTestContext, {
126
+ _local,
127
+ annotate,
128
+ expect,
129
+ onTestFailed,
130
+ onTestFinished,
131
+ signal,
132
+ skip,
133
+ task,
134
+ });
135
+ return runEffectTest(Effect.asVoid(run((harness, testContext) => body(item, harness, testContext), context)), context, clock);
136
+ },
137
+ );
138
+ },
139
+ fails: register(fixtureIt.fails),
140
+ only: register(fixtureIt.only),
141
+ runIf: (condition: unknown) => register(fixtureIt.runIf(condition)),
142
+ skip: register(fixtureIt.skip),
143
+ skipIf: (condition: unknown) => register(fixtureIt.skipIf(condition)),
144
+ });
244
145
 
245
146
  return { effectApp };
246
147
  };