@shivaedev/effect-test 0.7.2 → 0.9.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 - 2026-10-25
4
+
5
+ ### Added
6
+
7
+ - `@shivaedev/effect-test` readiness retries accept `timeout` and `wait` options. A timeout preserves the final typed failure; a custom wait lets adapters settle observable work through the same retry engine.
8
+
9
+ ### Breaking
10
+
11
+ - `@shivaedev/effect-react/client/testing.ts` now returns an Effect from `eventually(assert)`. Replace `yield* Effect.promise(() => eventually(assert))` with `yield* eventually(assert)` in stories and Effect tests, or replace `await eventually(assert)` with `await Effect.runPromise(eventually(assert))` in async tests. Assertions wait inside React `act` in live time, including under TestClock. Timeout errors include elapsed milliseconds and the last assertion message, with the assertion error as their `cause`.
12
+
13
+ ## 0.8.1 - 2026-10-25
14
+
15
+ ### Fixed
16
+
17
+ - Close shared test service Layers before their test file completes so database cleanup finishes before Vitest starts terminating the fork.
18
+
19
+ ## 0.8.0 - 2026-10-25
20
+
21
+ ### Breaking
22
+
23
+ - Move `@shivaedev/effect-test/vitest.ts` to `@shivaedev/effect-test/makeEffectIt.ts`. Change imports to `@shivaedev/effect-test/makeEffectIt.ts`; the exported `makeEffectIt` function is unchanged.
24
+
3
25
  ## 0.7.2 - 2026-10-24
4
26
 
5
27
  ### Changed
package/README.md CHANGED
@@ -27,9 +27,9 @@ That is a whole test. The body starts at time zero and advances its clock by one
27
27
 
28
28
  There are two ways to enter the runner. `it.effect` and `it.live` are the ordinary testers: their body receives Vitest's test context and uses services it provides inside its own Effect. `makeEffectIt` defines a tester for a reusable fixture: its body receives a harness first, then the test context, and can yield the services its Layer provides.
29
29
 
30
- A **Layer** describes how to acquire the fixture's services. `makeEffectIt` builds it in a Vitest worker fixture and retains the resulting Effect **Context**, the map from service keys to service values. A **harness** is the value made for one test using those services. It can hold a service, a fixture identifier or the domain entry points the test needs. The **test context** is Vitest's context, with the test's name, assertion helpers and hooks; it is a different thing from Effect's Context.
30
+ A **Layer** describes how to acquire the fixture's services. `makeEffectIt` builds it in a Vitest file fixture and retains the resulting Effect **Context**, the map from service keys to service values. A **harness** is the value made for one test using those services. It can hold a service, a fixture identifier or the domain entry points the test needs. The **test context** is Vitest's context, with the test's name, assertion helpers and hooks; it is a different thing from Effect's Context.
31
31
 
32
- Keep the two lifetimes separate. The worker owns the acquired Layer and its scope. Each test makes its harness and runs its body in a separate scope. Harnesses, wrappers, generator bodies and `each` bodies may acquire resources through that Scope; their finalizers run on success, failure and interruption. Scope is available per test and is not added to the worker Context. A shared service can retain mutable state across tests; a fresh harness alone does not reset it. Put application-state isolation in the fixture that owns that state, such as a database transaction wrapper.
32
+ Keep the two lifetimes separate. The file owns the acquired Layer and its scope; its finalizers finish before Vitest reports the file complete. Each test makes its harness and runs its body in a separate scope. Harnesses, wrappers, generator bodies and `each` bodies may acquire resources through that Scope; their finalizers run on success, failure and interruption. Scope is available per test and is not added to the file Context. A shared service can retain mutable state across tests; a fresh harness alone does not reset it. Put application-state isolation in the fixture that owns that state, such as a database transaction wrapper.
33
33
 
34
34
  The work usually splits into two parts:
35
35
 
@@ -41,7 +41,7 @@ The work usually splits into two parts:
41
41
  ```ts
42
42
  import { expect } from "@effect/vitest";
43
43
  import { Clock, Context, Effect, Layer } from "effect";
44
- import { makeEffectIt } from "@shivaedev/effect-test/vitest.ts";
44
+ import { makeEffectIt } from "@shivaedev/effect-test/makeEffectIt.ts";
45
45
 
46
46
  class Token extends Context.Service<Token, string>()("test/Token") {}
47
47
 
@@ -69,7 +69,7 @@ A fixture that needs no services uses `Layer.empty`. This separate module replac
69
69
  ```ts
70
70
  import { expect } from "@effect/vitest";
71
71
  import { Effect, Layer } from "effect";
72
- import { makeEffectIt } from "@shivaedev/effect-test/vitest.ts";
72
+ import { makeEffectIt } from "@shivaedev/effect-test/makeEffectIt.ts";
73
73
 
74
74
  const { effectApp } = makeEffectIt({
75
75
  layer: Layer.empty,
@@ -90,7 +90,7 @@ This alternative token fixture adds a tracing wrapper:
90
90
  ```ts
91
91
  import { expect } from "@effect/vitest";
92
92
  import { Context, Effect, Layer } from "effect";
93
- import { makeEffectIt } from "@shivaedev/effect-test/vitest.ts";
93
+ import { makeEffectIt } from "@shivaedev/effect-test/makeEffectIt.ts";
94
94
 
95
95
  class Token extends Context.Service<Token, string>()("test/Token") {}
96
96
 
@@ -107,7 +107,7 @@ effectApp("reads the harness", function* (token) {
107
107
 
108
108
  `around` receives the Effect that makes the harness and then runs the body. The wrapper also has the Layer's services. This is where a fixture composes its tracing or transaction boundary. The database package owns whether that transaction rolls back; this runner does not add a database policy. The later examples use the first token fixture, whose harness contains `token` and `name`.
109
109
 
110
- `@shivaedev/effect-prisma` and `@shivaedev/effect-trpc` build their testing helpers on this factory. `@shivaedev/test-story` builds domain stories above it and owns traits, verbs, engine stepping and failure narration. Use those helpers when the test needs their vocabulary; use this runner when defining the fixture underneath. An application-owned story kit can lease PostgreSQL through its worker Layer, use `around` for real-commit resets or local rollback, and acquire servers, native RPC clients and React roots through per-test Scope. The [order feature kit](https://github.com/ShivaeDev/platform/tree/main/packages/effect-react/src/test-support/order-example) demonstrates that composition; [test-story’s kit guide](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#whole-stack-application-kits) explains the application’s isolation, seed and screen responsibilities.
110
+ `@shivaedev/effect-prisma` and `@shivaedev/effect-trpc` build their testing helpers on this factory. `@shivaedev/test-story` builds domain stories above it and owns traits, verbs, engine stepping and failure narration. Use those helpers when the test needs their vocabulary; use this runner when defining the fixture underneath. An application-owned story kit can lease PostgreSQL through its file Layer, use `around` for real-commit resets or local rollback, and acquire servers, native RPC clients and React roots through per-test Scope. The [order feature kit](https://github.com/ShivaeDev/platform/tree/main/packages/effect-react/src/test-support/order-example) demonstrates that composition; [test-story’s kit guide](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#whole-stack-application-kits) explains the application’s isolation, seed and screen responsibilities.
111
111
 
112
112
  ### 2. In every test: the generator
113
113
 
@@ -163,7 +163,7 @@ Both test and live clock fixtures install a fresh native TestConsole for each te
163
163
 
164
164
  ### Waiting for a typed readiness condition
165
165
 
166
- `eventually(effect, options)` retries the Effect's typed failures until it succeeds or uses its retry budget. It uses the active Clock: under TestClock it advances time with `TestClock.adjust`; under live time it retries with `Schedule.spaced`.
166
+ `eventually(effect, options)` retries the Effect's typed failures until it succeeds or uses its retry budget. It uses the active Clock: under TestClock it advances time with `TestClock.adjust`; under live time it waits with `Effect.sleep`.
167
167
 
168
168
  ```ts
169
169
  import { eventually } from "@shivaedev/effect-test/eventually.ts";
@@ -181,7 +181,7 @@ effectApp("reaches the selected time", function* () {
181
181
  });
182
182
  ```
183
183
 
184
- The attempts see 0, 7, 14 and subsequent multiples of 7 through 56 milliseconds. `times` counts retries after the initial attempt, so `times: 0` tries once. When the budget runs out, the final typed failure remains the failure.
184
+ The attempts see 0, 7, 14 and subsequent multiples of 7 through 56 milliseconds. `times` counts retries after the initial attempt, so `times: 0` tries once. When the budget runs out, the final typed failure remains the failure. `timeout` sets an elapsed-time budget on the active clock, checked after a failed attempt. `wait` replaces the interval delay with a settlement Effect, so adapters such as React testing can own how pending work settles.
185
185
 
186
186
  A thrown assertion is a defect, so it escapes on the first attempt. Interruption also escapes immediately. Capture an assertion with `Effect.try` only when its failure means the state is not ready yet:
187
187
 
@@ -223,7 +223,7 @@ Import the module that defines the name; the package has no root entry.
223
223
  | Module | Exports |
224
224
  | --- | --- |
225
225
  | `@shivaedev/effect-test/it.ts` | `it`, with generator-aware `effect` and `live` testers. |
226
- | `@shivaedev/effect-test/vitest.ts` | `makeEffectIt({ layer, makeHarness, around?, clock? })`, returning `{ effectApp }`. |
226
+ | `@shivaedev/effect-test/makeEffectIt.ts` | `makeEffectIt({ layer, makeHarness, around?, clock? })`, returning `{ effectApp }`. |
227
227
  | `@shivaedev/effect-test/eventually.ts` | `eventually(effect, options?)` and `EventuallyOptions`. |
228
228
  | `@shivaedev/effect-test/withLive.ts` | `withLive(effect)`, a live-time region under either clock. |
229
229
  | `@shivaedev/effect-test/anyTestLayer.ts` | `AnyTestLayer` and `buildTestLayer(layer, scope)`, an Effect that builds the Layer's service Context in the supplied scope. |
@@ -1,7 +1,9 @@
1
- import { type Duration, Effect } from "effect";
1
+ import { Duration, Effect } from "effect";
2
2
  export interface EventuallyOptions {
3
3
  readonly interval?: Duration.Input;
4
+ readonly timeout?: Duration.Input;
4
5
  readonly times?: number;
6
+ readonly wait?: Effect.Effect<void>;
5
7
  }
6
- export declare const eventually: <A, E, R>(effect: Effect.Effect<A, E, R>, options?: EventuallyOptions) => Effect.Effect<A, E, R>;
8
+ export declare const eventually: <A, E, R>(effect: Effect.Effect<A, E, R>, options?: EventuallyOptions | undefined) => Effect.Effect<A, E, R>;
7
9
  //# sourceMappingURL=eventually.d.ts.map
@@ -1 +1 @@
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;AAoCD,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,YAAY,iBAAiB,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAMpH,CAAC"}
1
+ {"version":3,"file":"eventually.d.ts","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAGzD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,eAAO,MAAM,UAAU,GAAgC,CAAC,EAAE,CAAC,EAAE,CAAC,oGAwB5D,CAAC"}
@@ -1,39 +1,25 @@
1
- import { Clock, Effect, Result, Schedule } from "effect";
1
+ import { Clock, Duration, Effect, Result } from "effect";
2
2
  import * as TestClock from "effect/testing/TestClock";
3
- const defaultInterval = "10 millis";
4
- function isTestClock(clock) {
5
- return "adjust" in clock;
6
- }
7
- function retryOnLiveClock(effect, interval, times) {
8
- return times === undefined
9
- ? Effect.retry(effect, Schedule.spaced(interval))
10
- : Effect.retry(effect, {
11
- schedule: Schedule.spaced(interval),
12
- times,
13
- });
14
- }
15
- function retryOnTestClock(effect, interval, times) {
16
- return Effect.gen(function* () {
17
- let retriesLeft = times;
18
- for (;;) {
19
- const result = yield* Effect.result(effect);
20
- if (Result.isSuccess(result)) {
21
- return result.success;
22
- }
23
- if (retriesLeft === 0) {
24
- return yield* Effect.fail(result.failure);
25
- }
26
- if (retriesLeft !== undefined) {
27
- retriesLeft -= 1;
28
- }
29
- yield* TestClock.adjust(interval);
30
- }
31
- });
32
- }
33
- export const eventually = (effect, options) => Effect.gen(function* () {
34
- const interval = options?.interval ?? defaultInterval;
3
+ export const eventually = Effect.fnUntraced(function* (effect, options) {
35
4
  const clock = yield* Clock.Clock;
36
- const retry = isTestClock(clock) ? retryOnTestClock : retryOnLiveClock;
37
- return yield* retry(effect, interval, options?.times);
5
+ const started = yield* Clock.currentTimeMillis;
6
+ const timeout = options?.timeout === undefined ? undefined : Duration.toMillis(Duration.fromInputUnsafe(options.timeout));
7
+ const interval = options?.interval ?? "10 millis";
8
+ const wait = options?.wait ?? ("adjust" in clock ? TestClock.adjust(interval) : Effect.sleep(interval));
9
+ let retriesLeft = options?.times;
10
+ for (;;) {
11
+ const result = yield* Effect.result(effect);
12
+ if (Result.isSuccess(result)) {
13
+ return result.success;
14
+ }
15
+ const elapsed = (yield* Clock.currentTimeMillis) - started;
16
+ if (retriesLeft === 0 || (timeout !== undefined && elapsed >= timeout)) {
17
+ return yield* Effect.fail(result.failure);
18
+ }
19
+ if (retriesLeft !== undefined) {
20
+ retriesLeft -= 1;
21
+ }
22
+ yield* wait;
23
+ }
38
24
  });
39
25
  //# 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,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACxE,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AAOtD,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC,SAAS,WAAW,CAAC,KAAkB;IACtC,OAAO,QAAQ,IAAI,KAAK,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAU,MAA8B,EAAE,QAAwB,EAAE,KAAyB;IACrH,OAAO,KAAK,KAAK,SAAS;QACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;YACrB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;YACnC,KAAK;SACL,CAAC,CAAC;AACN,CAAC;AAED,SAAS,gBAAgB,CAAU,MAA8B,EAAE,QAAwB,EAAE,KAAyB;IACrH,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,SAAS,CAAC;YACT,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,OAAO,MAAM,CAAC,OAAO,CAAC;YACvB,CAAC;YACD,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC/B,WAAW,IAAI,CAAC,CAAC;YAClB,CAAC;YACD,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,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"}
1
+ {"version":3,"file":"eventually.js","sourceRoot":"","sources":["../src/eventually.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AAStD,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EACpD,MAA8B,EAC9B,OAA2B;IAE3B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;IAC/C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1H,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,WAAW,CAAC;IAClD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxG,IAAI,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC;IACjC,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,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC;QAC3D,IAAI,WAAW,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC;YACxE,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,IAAI,CAAC;IACb,CAAC;AACF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type Layer } from "effect";
2
+ import { type AnyTestLayer } from "./anyTestLayer.ts";
3
+ import type { MakeEffectItOptions, MakeEffectItResult } from "./types.ts";
4
+ export declare function makeEffectIt<THarness, TTestLayer extends AnyTestLayer>(options: MakeEffectItOptions<THarness, TTestLayer>): MakeEffectItResult<THarness, Layer.Success<TTestLayer>>;
5
+ //# sourceMappingURL=makeEffectIt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeEffectIt.d.ts","sourceRoot":"","sources":["../src/makeEffectIt.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,KAAK,EAAS,MAAM,QAAQ,CAAC;AAEvE,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAA4E,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgDpJ,wBAAgB,YAAY,CAAC,QAAQ,EAAE,UAAU,SAAS,YAAY,EACrE,OAAO,EAAE,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,GAChD,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAuEzD"}
@@ -2,11 +2,11 @@ import { it as effectIt } from "@effect/vitest";
2
2
  import { Effect, Exit, Scope } from "effect";
3
3
  import { runEffectTest } from "#internal/runEffectTest.ts";
4
4
  import { buildTestLayer } from "./anyTestLayer.js";
5
- const fixtureName = "__effectTestContext";
5
+ const FIXTURE_NAME = "__effectTestContext";
6
6
  function restoreContext(fixture, context) {
7
7
  return {
8
8
  ...context,
9
- [fixtureName]: fixture,
9
+ [FIXTURE_NAME]: fixture,
10
10
  };
11
11
  }
12
12
  function splitOptions(options) {
@@ -20,7 +20,7 @@ function splitOptions(options) {
20
20
  return { clock, vitest };
21
21
  }
22
22
  function makeFixtureIt(layer) {
23
- return effectIt.extend(fixtureName, { scope: "worker" }, async ({}, { onCleanup }) => {
23
+ return effectIt.extend(FIXTURE_NAME, { scope: "file" }, async ({}, { onCleanup }) => {
24
24
  const scope = Effect.runSync(Scope.make());
25
25
  onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
26
26
  try {
@@ -32,7 +32,7 @@ function makeFixtureIt(layer) {
32
32
  }
33
33
  });
34
34
  }
35
- export const makeEffectIt = (options) => {
35
+ export function makeEffectIt(options) {
36
36
  const fixtureIt = makeFixtureIt(options.layer);
37
37
  const defaultClock = options.clock ?? "test";
38
38
  function run(body, context) {
@@ -42,7 +42,7 @@ export const makeEffectIt = (options) => {
42
42
  return yield* Effect.isEffect(result) ? result : Effect.gen(() => result);
43
43
  });
44
44
  const wrapped = options.around?.(ready) ?? ready;
45
- return wrapped.pipe(Effect.provide(context[fixtureName]));
45
+ return wrapped.pipe(Effect.provide(context[FIXTURE_NAME]));
46
46
  }
47
47
  function register(current) {
48
48
  return (name, body, testOptions) => {
@@ -88,5 +88,5 @@ export const makeEffectIt = (options) => {
88
88
  skipIf: (condition) => register(fixtureIt.skipIf(condition)),
89
89
  });
90
90
  return { effectApp };
91
- };
92
- //# sourceMappingURL=vitest.js.map
91
+ }
92
+ //# sourceMappingURL=makeEffectIt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeEffectIt.js","sourceRoot":"","sources":["../src/makeEffectIt.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shivaedev/effect-test",
3
- "version": "0.7.2",
4
- "description": "Generic Effect Vitest runner with worker-scoped Layers and TestClock",
3
+ "version": "0.9.0",
4
+ "description": "Generic Effect Vitest runner with file-scoped Layers and TestClock",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-test#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/ShivaeDev/platform/issues"
@@ -42,7 +42,7 @@
42
42
  ],
43
43
  "devDependencies": {
44
44
  "@effect/vitest": "4.0.5",
45
- "@shivaedev/quality": "0.34.0",
45
+ "@shivaedev/quality": "0.39.1",
46
46
  "@types/node": "24.19.2",
47
47
  "effect": "4.0.5",
48
48
  "typescript": "7.0.2",
package/src/eventually.ts CHANGED
@@ -1,49 +1,35 @@
1
- import { Clock, type Duration, Effect, Result, Schedule } from "effect";
1
+ import { Clock, Duration, Effect, Result } from "effect";
2
2
  import * as TestClock from "effect/testing/TestClock";
3
3
 
4
4
  export interface EventuallyOptions {
5
5
  readonly interval?: Duration.Input;
6
+ readonly timeout?: Duration.Input;
6
7
  readonly times?: number;
8
+ readonly wait?: Effect.Effect<void>;
7
9
  }
8
10
 
9
- const defaultInterval = "10 millis";
10
-
11
- function isTestClock(clock: Clock.Clock): clock is TestClock.TestClock {
12
- return "adjust" in clock;
13
- }
14
-
15
- function retryOnLiveClock<A, E, R>(effect: Effect.Effect<A, E, R>, interval: Duration.Input, times: number | undefined): Effect.Effect<A, E, R> {
16
- return times === undefined
17
- ? Effect.retry(effect, Schedule.spaced(interval))
18
- : Effect.retry(effect, {
19
- schedule: Schedule.spaced(interval),
20
- times,
21
- });
22
- }
23
-
24
- function retryOnTestClock<A, E, R>(effect: Effect.Effect<A, E, R>, interval: Duration.Input, times: number | undefined): Effect.Effect<A, E, R> {
25
- return Effect.gen(function* () {
26
- let retriesLeft = times;
27
- for (;;) {
28
- const result = yield* Effect.result(effect);
29
- if (Result.isSuccess(result)) {
30
- return result.success;
31
- }
32
- if (retriesLeft === 0) {
33
- return yield* Effect.fail(result.failure);
34
- }
35
- if (retriesLeft !== undefined) {
36
- retriesLeft -= 1;
37
- }
38
- yield* TestClock.adjust(interval);
11
+ export const eventually = Effect.fnUntraced(function* <A, E, R>(
12
+ effect: Effect.Effect<A, E, R>,
13
+ options?: EventuallyOptions,
14
+ ): Effect.fn.Return<A, E, R> {
15
+ const clock = yield* Clock.Clock;
16
+ const started = yield* Clock.currentTimeMillis;
17
+ const timeout = options?.timeout === undefined ? undefined : Duration.toMillis(Duration.fromInputUnsafe(options.timeout));
18
+ const interval = options?.interval ?? "10 millis";
19
+ const wait = options?.wait ?? ("adjust" in clock ? TestClock.adjust(interval) : Effect.sleep(interval));
20
+ let retriesLeft = options?.times;
21
+ for (;;) {
22
+ const result = yield* Effect.result(effect);
23
+ if (Result.isSuccess(result)) {
24
+ return result.success;
39
25
  }
40
- });
41
- }
42
-
43
- export const eventually = <A, E, R>(effect: Effect.Effect<A, E, R>, options?: EventuallyOptions): Effect.Effect<A, E, R> =>
44
- Effect.gen(function* () {
45
- const interval = options?.interval ?? defaultInterval;
46
- const clock = yield* Clock.Clock;
47
- const retry = isTestClock(clock) ? retryOnTestClock : retryOnLiveClock;
48
- return yield* retry(effect, interval, options?.times);
49
- });
26
+ const elapsed = (yield* Clock.currentTimeMillis) - started;
27
+ if (retriesLeft === 0 || (timeout !== undefined && elapsed >= timeout)) {
28
+ return yield* Effect.fail(result.failure);
29
+ }
30
+ if (retriesLeft !== undefined) {
31
+ retriesLeft -= 1;
32
+ }
33
+ yield* wait;
34
+ }
35
+ });
@@ -4,21 +4,21 @@ import { runEffectTest } from "#internal/runEffectTest.ts";
4
4
  import { type AnyTestLayer, buildTestLayer } from "./anyTestLayer.ts";
5
5
  import type { EffectClock, EffectTest, EffectTestBody, EffectTester, EffectTestOptions, MakeEffectItOptions, MakeEffectItResult } from "./types.ts";
6
6
 
7
- const fixtureName = "__effectTestContext";
7
+ const FIXTURE_NAME = "__effectTestContext";
8
8
 
9
9
  type EffectFixture<TProvided> = TestContext & {
10
- readonly [fixtureName]: Context.Context<TProvided>;
10
+ readonly [FIXTURE_NAME]: Context.Context<TProvided>;
11
11
  };
12
12
 
13
13
  type FixtureTest<TProvided> = (name: string, options: TestOptions, body: (context: EffectFixture<TProvided>) => Promise<unknown>) => void;
14
14
 
15
15
  function restoreContext<TProvided>(
16
16
  fixture: Context.Context<TProvided>,
17
- context: Omit<EffectFixture<TProvided>, typeof fixtureName>,
17
+ context: Omit<EffectFixture<TProvided>, typeof FIXTURE_NAME>,
18
18
  ): EffectFixture<TProvided> {
19
19
  return {
20
20
  ...context,
21
- [fixtureName]: fixture,
21
+ [FIXTURE_NAME]: fixture,
22
22
  };
23
23
  }
24
24
 
@@ -37,7 +37,7 @@ function splitOptions(options: number | EffectTestOptions | undefined): {
37
37
  }
38
38
 
39
39
  function makeFixtureIt<TLayer extends AnyTestLayer>(layer: TLayer) {
40
- return effectIt.extend(fixtureName, { scope: "worker" }, async ({}, { onCleanup }) => {
40
+ return effectIt.extend(FIXTURE_NAME, { scope: "file" }, async ({}, { onCleanup }) => {
41
41
  const scope = Effect.runSync(Scope.make());
42
42
  onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
43
43
 
@@ -50,9 +50,9 @@ function makeFixtureIt<TLayer extends AnyTestLayer>(layer: TLayer) {
50
50
  });
51
51
  }
52
52
 
53
- export const makeEffectIt = <THarness, TTestLayer extends AnyTestLayer>(
53
+ export function makeEffectIt<THarness, TTestLayer extends AnyTestLayer>(
54
54
  options: MakeEffectItOptions<THarness, TTestLayer>,
55
- ): MakeEffectItResult<THarness, Layer.Success<TTestLayer>> => {
55
+ ): MakeEffectItResult<THarness, Layer.Success<TTestLayer>> {
56
56
  type Provided = Layer.Success<TTestLayer>;
57
57
  const fixtureIt = makeFixtureIt(options.layer);
58
58
  const defaultClock = options.clock ?? "test";
@@ -64,7 +64,7 @@ export const makeEffectIt = <THarness, TTestLayer extends AnyTestLayer>(
64
64
  return yield* Effect.isEffect(result) ? result : Effect.gen(() => result);
65
65
  });
66
66
  const wrapped = options.around?.(ready) ?? ready;
67
- return wrapped.pipe(Effect.provide(context[fixtureName]));
67
+ return wrapped.pipe(Effect.provide(context[FIXTURE_NAME]));
68
68
  }
69
69
 
70
70
  function register(current: FixtureTest<Provided>): EffectTest<THarness, Provided> {
@@ -123,4 +123,4 @@ export const makeEffectIt = <THarness, TTestLayer extends AnyTestLayer>(
123
123
  });
124
124
 
125
125
  return { effectApp };
126
- };
126
+ }
package/dist/vitest.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { type Layer } from "effect";
2
- import { type AnyTestLayer } from "./anyTestLayer.ts";
3
- import type { MakeEffectItOptions, MakeEffectItResult } from "./types.ts";
4
- export declare const makeEffectIt: <THarness, TTestLayer extends AnyTestLayer>(options: MakeEffectItOptions<THarness, TTestLayer>) => MakeEffectItResult<THarness, Layer.Success<TTestLayer>>;
5
- //# sourceMappingURL=vitest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,KAAK,EAAS,MAAM,QAAQ,CAAC;AAEvE,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAA4E,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgDpJ,eAAO,MAAM,YAAY,GAAI,QAAQ,EAAE,UAAU,SAAS,YAAY,WAC5D,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,KAChD,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAuExD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}