@shivaedev/test-story 0.7.0 → 0.8.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +103 -69
  3. package/dist/server/domEnvironment.config.js +1 -1
  4. package/dist/server/domEnvironment.config.js.map +1 -1
  5. package/dist/server/internal/installOutbound.d.ts +2 -0
  6. package/dist/server/internal/installOutbound.d.ts.map +1 -0
  7. package/dist/server/internal/{routeOutbound.js → installOutbound.js} +1 -5
  8. package/dist/server/internal/installOutbound.js.map +1 -0
  9. package/dist/server/internal/screenElements.d.ts +9 -0
  10. package/dist/server/internal/screenElements.d.ts.map +1 -0
  11. package/dist/server/internal/screenElements.js +61 -0
  12. package/dist/server/internal/screenElements.js.map +1 -0
  13. package/dist/server/internal/seed.d.ts +1 -1
  14. package/dist/server/internal/seed.d.ts.map +1 -1
  15. package/dist/server/internal/start.d.ts.map +1 -1
  16. package/dist/server/internal/start.js +4 -1
  17. package/dist/server/internal/start.js.map +1 -1
  18. package/dist/server/internal/storyIt.d.ts.map +1 -1
  19. package/dist/server/internal/storyIt.js +1 -1
  20. package/dist/server/internal/storyIt.js.map +1 -1
  21. package/dist/server/{internal/routeOutbound.d.ts → routeOutbound.d.ts} +0 -1
  22. package/dist/server/routeOutbound.d.ts.map +1 -0
  23. package/dist/server/routeOutbound.js +6 -0
  24. package/dist/server/routeOutbound.js.map +1 -0
  25. package/dist/server/screenDriver.d.ts +24 -0
  26. package/dist/server/screenDriver.d.ts.map +1 -0
  27. package/dist/server/screenDriver.js +60 -0
  28. package/dist/server/screenDriver.js.map +1 -0
  29. package/dist/server/storyKit.d.ts +3 -3
  30. package/dist/server/storyKit.d.ts.map +1 -1
  31. package/package.json +6 -8
  32. package/src/server/domEnvironment.config.ts +1 -1
  33. package/src/server/internal/{routeOutbound.ts → installOutbound.ts} +0 -8
  34. package/src/server/internal/screenElements.ts +62 -0
  35. package/src/server/internal/seed.ts +1 -1
  36. package/src/server/internal/start.ts +9 -1
  37. package/src/server/internal/storyIt.ts +1 -1
  38. package/src/server/routeOutbound.ts +9 -0
  39. package/src/server/screenDriver.ts +75 -0
  40. package/src/server/storyKit.ts +5 -5
  41. package/dist/server/internal/routeOutbound.d.ts.map +0 -1
  42. package/dist/server/internal/routeOutbound.js.map +0 -1
  43. package/docs/north-star.md +0 -35
  44. package/docs/roadmap.md +0 -26
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0 - 2026-10-18
4
+
5
+ ### Added
6
+
7
+ - `screenDriver(options).accessible(selector)` returns and narrates an element's computed accessible name and role, including label references that take precedence over `aria-label`.
8
+ - Export `screenDriver(options)`, `ScreenDriverOptions` and `ScreenDriver` from `screenDriver.ts`: narrated DOM clicks, text entry, form submission and screen readouts, with settlement driven by the app's pending signal and an optional renderer `act` adapter. Screens use the existing DOM transport to reach fake HTTP services, including identity providers.
9
+ - Screen drivers accept a root getter for document navigation and a per-call `settle({ timeoutMs })` limit.
10
+ - Export `routeOutbound(routes)` from `routeOutbound.ts` to scope outbound routing alongside a story's fake HTTP service.
11
+ - Screen actions and readouts reject hidden targets and targets inside hidden ancestors; form submission also refuses inert ancestors.
12
+ - Accept `stages: []` in `storyKit` definitions for engines initialized entirely in `create`, preserving inferred engine and verb types with no available trait stages.
13
+
14
+ ## 0.7.1 - 2026-10-18
15
+
16
+ ### Fixed
17
+
18
+ - Publish against `@shivaedev/effect-test` 0.6.0 so framework consumers share the current test runner.
19
+
3
20
  ## 0.7.0 - 2026-10-18
4
21
 
5
22
  ### Added
package/README.md CHANGED
@@ -8,11 +8,13 @@ When agents write most of the code, test files grow into hundreds of lines nobod
8
8
 
9
9
  ```ts
10
10
  bakery.it("steps until the condition holds", [ovenIsLit(), hasDough(3)], function* ({ oven }) {
11
- expect(yield* oven.bakesEverything()).toEqual({ loaves: 3, minutes: 3 });
11
+ yield* oven.bakesEverything();
12
+ expect(oven.loaves()).toBe(3);
13
+ expect(oven.minutes()).toBe(3);
12
14
  });
13
15
  ```
14
16
 
15
- That is a whole test. `bakery.it` comes from the bakery's story kit. The list holds its traits, `ovenIsLit()` and `hasDough(3)`, which are written once and shared by every spec. `oven.bakesEverything()` is a verb that runs the bakery's real code until the dough is gone. Every story runs the whole engine, so each test also checks the parts its author never thought to mock, and because a spec reads as English, a sloppy test stands out in review.
17
+ That is a whole test. `bakery.it` comes from the bakery's story kit. The list holds its traits, `ovenIsLit()` and `hasDough(3)`, which are written once and shared by every spec. `oven.bakesEverything()` performs the baking action until the dough is gone; `oven.loaves()` and `oven.minutes()` read separate observations. The spec states both expectations. Every story runs the whole engine, so each test also checks the parts its author never thought to mock, and because a spec reads as English, a sloppy test stands out in review.
16
18
 
17
19
  When a story fails, it prints itself. One of this package's own failing stories expects 2 loaves from one ball of dough, and the test fails with this message:
18
20
 
@@ -28,7 +30,7 @@ loaves: expected 1 to be 2 // Object.is equality
28
30
  given the baker has 1 balls of dough
29
31
  1m a loaf comes out of the oven
30
32
  ✗ the test failed after the line above
31
- at src/test-support/failingStories.ts:10:59
33
+ at src/test-support/failingStories.ts:12:34
32
34
 
33
35
  The bakery when the test failed:
34
36
  {"bowls":1,"capacity":3,"dough":0,"flour":0,"loaves":1,"minute":1,"orders":[],"ovenLit":true,"overfired":false,
@@ -57,6 +59,22 @@ The work splits into three layers, and each is written by a different hand at a
57
59
  2. **Once per feature**, traits and verbs in the kit name that feature in domain words.
58
60
  3. **In every test**, a spec tells a story with the kit's `it` and checks what a user would see.
59
61
 
62
+ ### The verb shape every kit follows
63
+
64
+ A verb performs one action on the real engine or reads one observation. An action may return its real response, such as an HTTP response; it does not orchestrate a scenario and package the measurements for the spec. A readout reports what happened without deciding whether it was correct. The spec shows each action in order and states every expectation.
65
+
66
+ Acquire real services in `create` or a trait inside the story's per-test Scope, with dependencies provided by `layer`. Keep those services on the engine so verbs use the same running instance; scoped cleanup releases them after the story. Do not build and tear down services inside each verb. When closing a service is the action under test, expose that action separately from acquisition and observation.
67
+
68
+ | Shape to avoid | What to do instead |
69
+ | --- | --- |
70
+ | A verb runs a whole scenario and returns the measurements the spec asserts. | Give each action and observation its own verb; let the spec compose them and compare the observations. |
71
+ | A verb hard-codes the expected outcome or hides assertions. | Return the real response or observation, and put expected values and all assertions in the spec. |
72
+ | A verb does two jobs and returns both results. | Split the jobs into two verbs and show both calls in the spec. |
73
+ | A verb silently does nothing when its target is missing, such as `button?.click()`. | Fail with a message naming the missing target, then perform the action only once the target exists. |
74
+ | An engine default hides what the story declares, such as a lit oven, signed-in user or authorization header. | Start with a neutral engine and name the precondition in a trait or pass the input explicitly in the action. |
75
+
76
+ The bakery below starts with a cold oven and no dough. Its traits declare heat and dough, its baking action returns no report, and its readouts expose loaves and elapsed minutes separately. `runUntil` waits for that one engine action to complete; it does not replace a spec's sequence of actions.
77
+
60
78
  ### 1. Once per engine: the story kit
61
79
 
62
80
  `storyKit(definition)` from `@shivaedev/test-story/storyKit.ts` turns one definition into the kit. Every type, the engine's, the stages', the verbs' and the services', is inferred from it.
@@ -97,8 +115,10 @@ export const bakery = storyKit({
97
115
  },
98
116
  },
99
117
  oven: {
100
- bakesEverything: (maxSteps?: number): Effect.Effect<BakeReport, BurntLoaf> =>
101
- story.runUntil((current) => current.dough === 0, { maxSteps }).pipe(Effect.map(() => ({ loaves: state.loaves, minutes: state.minute }))),
118
+ bakesEverything: (maxSteps?: number): Effect.Effect<void, BurntLoaf> =>
119
+ story.runUntil((current) => current.dough === 0, { maxSteps }),
120
+ loaves: () => state.loaves,
121
+ minutes: () => state.minute,
102
122
  },
103
123
  }),
104
124
  });
@@ -118,7 +138,7 @@ This is the bakery kit this package tests itself with. `emptyBakery`, `stillWait
118
138
  | `after` | Optional. A hook per stage that runs once that stage's traits are applied, even when no trait names the stage, so a value it derives is ready before a later stage reads it. Its typed failure fails the test. |
119
139
  | `run` | Optional. What `story.runUntil` needs: `step(engine, tell)` advances the engine once and tells what happened, `maxSteps` caps one run, `failed(engine)` returns a sentence when the engine is in a state it cannot recover from, and `diagnose(engine)` says why it has not finished. |
120
140
  | `inspect` | Optional. The part of the engine a failure prints. Without it, the failure prints the whole engine. |
121
- | `verbs(engine, story)` | The entry points a test acts through. A verb that runs the engine returns the Effect from `story.runUntil`. |
141
+ | `verbs(engine, story)` | One action or one observation per verb. A verb that waits for a stepped engine returns the Effect from `story.runUntil`; the spec reads observations and asserts separately. |
122
142
 
123
143
  Every test of the kit is named `Bakery Story: <its name>`, so `vitest run -t "Bakery Story: "` runs only the bakery's stories with no setup. A project that splits runs by Vitest tags can also tag them:
124
144
 
@@ -146,7 +166,9 @@ const app = storyKit({
146
166
 
147
167
  `appDefinition` holds the application's kit hooks, and `fakeUrl` is the listening fake server's HTTP or HTTPS origin, such as `http://127.0.0.1:4321`. The map matches exact URL hostnames, across ports. It replaces the destination origin while keeping the request's path, query, method, headers and body. For example, a POST to `https://github.com/login/oauth/access_token` reaches the fake's `/login/oauth/access_token`. In Node stories, other hostnames pass through the previous dispatcher unchanged. In the DOM environment, only loopback hosts (`localhost`, IPv4 loopback addresses and `[::1]`) may pass through without a route; every other hostname must have an `outbound` fake. A refused request fails the story and names its hostname, even when application code catches the fetch error.
148
168
 
149
- Routing uses the global dispatcher that Node's built-in `fetch` already uses. It is installed before `around`, creation, traits and the body, stays active during scoped cleanup, and restores the previous dispatcher after each story, including a failing story. Stories within one worker must run one at a time because that dispatcher is shared; parallelism belongs between workers or inside one story. The DOM environment uses the same routing for host-side Node fetch and document requests, including redirect destinations. It refuses external hosts even when the kit omits `outbound`.
169
+ `routeOutbound(routes)` from `@shivaedev/test-story/routeOutbound.ts` installs the same routing in the current Effect Scope. Acquire a `fakeHttpService` in `create`, then yield `routeOutbound({ "api.example.com": api.url })` so the fake listener and its routing belong to that story. The starter's social sign-in story exercises this path through both document navigation and host-side requests.
170
+
171
+ Routing uses the global dispatcher that Node's built-in `fetch` already uses. The kit's `outbound` option installs routing before `around`, creation, traits and the body, stays active during scoped cleanup, and restores the previous dispatcher after each story, including a failing story. Stories within one worker must run one at a time because that dispatcher is shared; parallelism belongs between workers or inside one story. The DOM environment uses the same routing for host-side Node fetch and document requests, including redirect destinations. It refuses external hosts even when the kit omits `outbound`.
150
172
 
151
173
  #### Asserting on application traces
152
174
 
@@ -154,7 +176,7 @@ Start an in-process host in `create` or a stage's `after` hook, such as `after:
154
176
 
155
177
  This lets an application's story assert that a request starts its own trace: a request without trace headers starts a root trace, and a request with `traceparent` continues that trace. Host startup and cleanup do not join the story's trace.
156
178
 
157
- A kit can collect spans with a native in-memory tracer, or start a fake OTLP receiver and point the application's exporter at it. Keep collection and any exporter flush or bounded wait in the kit's verbs. Assert only after the request span has ended. This small in-memory kit uses Effect's Web Request handler as its host:
179
+ A kit can collect spans with a native in-memory tracer, or start a fake OTLP receiver and point the application's exporter at it. Expose collection, exporter flush and any bounded wait as separate verbs. Assert only after the request span has ended. This small in-memory kit uses Effect's Web Request handler as its host:
158
180
 
159
181
  ```ts
160
182
  import { Effect, Tracer } from "effect";
@@ -162,65 +184,69 @@ import { HttpEffect, HttpServerResponse } from "effect/http";
162
184
  import { expect } from "vitest";
163
185
  import { storyKit } from "@shivaedev/test-story/storyKit.ts";
164
186
 
165
- const spans: Tracer.NativeSpan[] = [];
166
- const tracer = Tracer.make({
167
- span: (options) => {
168
- const span = new Tracer.NativeSpan(options);
169
- spans.push(span);
170
- return span;
171
- },
172
- });
173
-
174
187
  const app = storyKit({
175
- around: (effect) => effect.pipe(Effect.withSpan("story"), Effect.withTracer(tracer)),
188
+ around: (effect) => effect.pipe(Effect.withSpan("story")),
176
189
  *create() {
177
- const context = yield* Effect.context<never>();
178
- return HttpEffect.toWebHandlerWith(context)(Effect.succeed(HttpServerResponse.text("ok")));
190
+ const spans: Tracer.NativeSpan[] = [];
191
+ const tracer = Tracer.make({
192
+ span: (options) => {
193
+ const span = new Tracer.NativeSpan(options);
194
+ spans.push(span);
195
+ return span;
196
+ },
197
+ });
198
+ const context = yield* Effect.context<never>().pipe(Effect.withTracer(tracer));
199
+ const handler = HttpEffect.toWebHandlerWith(context)(Effect.succeed(HttpServerResponse.text("ok")));
200
+ return { handler, spans };
179
201
  },
180
202
  name: "traced application",
181
203
  stages: ["ready"],
182
- verbs: (handler) => ({
204
+ verbs: ({ handler, spans }) => ({
183
205
  request: (headers: HeadersInit = {}) => Effect.promise(async () => {
184
206
  const response = await handler(new Request("http://app.test/", { headers }));
185
- await response.text();
207
+ return response.text();
208
+ }),
209
+ requestSpan: () => {
186
210
  const span = spans.filter((span) => span.kind === "server").at(-1);
187
211
  if (span === undefined) throw new Error("The request exported no server span");
188
212
  return span;
189
- }),
213
+ },
190
214
  }),
191
215
  });
192
216
 
193
- app.it("a request is its own root trace", [], function* ({ request }) {
217
+ app.it("a request is its own root trace", [], function* ({ request, requestSpan }) {
194
218
  const storySpan = yield* Effect.currentSpan;
195
- const span = yield* request();
219
+ expect(yield* request()).toBe("ok");
220
+ const span = requestSpan();
196
221
  expect(span.parent._tag).toBe("None");
197
222
  expect(span.traceId).not.toBe(storySpan.traceId);
198
223
  expect(span.status._tag).toBe("Ended");
199
224
  });
200
225
 
201
- app.it("a request continues its supplied trace", [], function* ({ request }) {
202
- const span = yield* request({ traceparent: "00-1234567890abcdef1234567890abcdef-1234567890abcdef-01" });
226
+ app.it("a request continues its supplied trace", [], function* ({ request, requestSpan }) {
227
+ yield* request({ traceparent: "00-1234567890abcdef1234567890abcdef-1234567890abcdef-01" });
228
+ const span = requestSpan();
203
229
  expect(span.traceId).toBe("1234567890abcdef1234567890abcdef");
204
230
  expect(span.parent).toMatchObject({ value: { spanId: "1234567890abcdef" } });
205
231
  });
206
232
  ```
207
233
 
208
- The package's [tracing kit](https://github.com/ShivaeDev/platform/blob/main/packages/test-story/src/test-support/tracingApp.ts) and [stories](https://github.com/ShivaeDev/platform/blob/main/packages/test-story/src/server/hostTracing.spec.ts) also assert that two requests have different trace IDs, request work stays under its request span, and traits and engine steps stay under the story span. The [after-hook stories](https://github.com/ShivaeDev/platform/blob/main/packages/test-story/src/server/afterHostTracing.spec.ts) boot a loopback HTTP host after deployment and check its startup, requests and cleanup against the story trace, while its request verb remains a child of the story span. Detachment only removes the incoming parent: spans that application startup explicitly creates still follow native Effect semantics.
234
+ Detachment only removes the incoming parent: spans that application startup explicitly creates still follow native Effect semantics.
209
235
 
210
236
  #### Reading OTLP exports
211
237
 
212
238
  Use `otlpReceiver` from `@shivaedev/test-story/otlpReceiver.ts` in a kit's scoped `create` hook. It listens on an allocated loopback port and closes with the story scope. Point the application's real exporter at `${receiver.url}/v1/traces`, then flush the exporter before reading:
213
239
 
214
240
  ```ts
215
- import { otlpReceiver } from "@shivaedev/test-story/otlpReceiver.ts";
216
-
217
- const receiver = yield* otlpReceiver();
218
- yield* exportWork(`${receiver.url}/v1/traces`);
219
- expect(receiver.spans).toContainEqual(expect.objectContaining({ name: "CountWords" }));
220
- expect(receiver.requests.some(({ body }) => body.includes(Buffer.from("planted-secret")))).toBe(true);
241
+ app.it("exports the work span", [], function* ({ runWork, disposeRuntime, spans, requests }) {
242
+ yield* runWork();
243
+ yield* disposeRuntime();
244
+ expect(spans()).toContainEqual(expect.objectContaining({ name: "CountWords" }));
245
+ expect(requests().some(({ body }) => body.includes(Buffer.from("planted-secret")))).toBe(true);
246
+ });
221
247
  ```
222
248
 
223
- `exportWork` is the kit's verb that runs application work and flushes its exporter. The receiver accepts OTLP/HTTP JSON and protobuf. `spans` flattens all exports into decoded trace and span IDs, parent span IDs, names, numeric OTLP kinds and status codes, attributes, events and links. Attributes retain OTLP key/value objects, including lossless decimal strings for integers; each span also has `resourceAttributes`. `requests` retains every request's unmodified `body` Buffer, parsed `headers`, original `rawHeaders` pairs and `path`. Search all body bytes when checking that a secret was not exported. Optional `path`, `status` (a number or attempt callback) and `answer` configure collector responses for failure stories.
249
+ Here `app` is the application's kit. Its `create` acquires the receiver and the real exporter pointed at that receiver, `runWork` performs the traced operation, `disposeRuntime` disposes the exporter's runtime and flushes pending spans, and `spans` and `requests` read the receiver's observations. Each action is visible in the spec. The receiver accepts OTLP/HTTP JSON and protobuf. `spans` flattens all exports into decoded trace and span IDs, parent span IDs, names, numeric OTLP kinds and status codes, attributes, events and links. Attributes retain OTLP key/value objects, including lossless decimal strings for integers; each span also has `resourceAttributes`. `requests` retains every request's unmodified `body` Buffer, parsed `headers`, original `rawHeaders` pairs and `path`. Search all body bytes when checking that a secret was not exported. Optional `path`, `status` (a number or attempt callback) and `answer` configure collector responses for failure stories.
224
250
 
225
251
  For timeout stories, return `undefined` from the `status` callback to accept an export without sending response headers. Set `answer: null` to send headers without completing the response body.
226
252
 
@@ -258,8 +284,6 @@ const user = yield* Fiber.join(pending);
258
284
 
259
285
  A `pages` route accepts a one-based `?page=` query and returns `{ items, next }`, with `next: null` on the last page. No query means page 1; a page past the end returns an empty list; a non-positive or non-integer page gets 400. `api.requests` reads a snapshot of the request log, including rejected and held requests, with `method`, `path` (including query), raw `body` Buffer, `headers`, `params` and `query`.
260
286
 
261
- The [fake HTTP stories](https://github.com/ShivaeDev/platform/blob/main/packages/test-story/src/server/outboundApi.spec.ts) use Node fetch to check params, credentials, pages, one-shot failure, hold/release ordering, request logs, narration and listener cleanup.
262
-
263
287
  ### 2. Once per feature: traits and verbs
264
288
 
265
289
  A trait is a line and a change, tagged with a stage:
@@ -303,7 +327,7 @@ help: a trait throws when the bakery it asks for cannot exist. Give the test tra
303
327
 
304
328
  given the oven is lit
305
329
  ✗ given the baker has 4 balls of dough
306
- refused at src/test-support/failingStories.ts:13:57
330
+ refused at src/test-support/failingStories.ts:15:57
307
331
  given the bakery has 1 bowls
308
332
 
309
333
  The bakery when the test failed:
@@ -370,11 +394,11 @@ The package wraps between words or JSON fields toward 120 columns and prints the
370
394
 
371
395
  ### Library package kits
372
396
 
373
- A library package has an engine too: its own services or Layer under test. Keep its kit in `src/test-support/`, acquire the real services in `create` with their dependencies supplied by the kit's `layer`, and use real PostgreSQL wherever the package touches it. Traits seed that engine in domain words; verbs call the package's public API. The same division applies as for an application: the kit owns setup and wiring, and a spec names traits, calls verbs and checks results. Following the [root test guidance](https://github.com/ShivaeDev/platform/blob/main/AGENTS.md), reserve plain Vitest tests for pure functions with no engine behind them.
397
+ A library package has an engine too: its own services or Layer under test. Keep its kit in `src/test-support/`, acquire the real services in `create` or a trait inside the story's per-test Scope, with their dependencies supplied by the kit's `layer`, and use real PostgreSQL wherever the package touches it. Traits seed that engine in domain words; each verb calls one action on the package's public API or reads one observation from that same engine. Verbs never create a fresh service just to run a scenario and return its measurements. The same division applies as for an application: the kit owns setup and wiring, and a spec names traits, shows every action through verbs and states each expectation. Following the [root test guidance](https://github.com/ShivaeDev/platform/blob/main/AGENTS.md), reserve plain Vitest tests for pure functions with no engine behind them.
374
398
 
375
399
  The [`backfill` kit](https://github.com/ShivaeDev/platform/blob/main/packages/effect-test-sql/src/test-support/migrationStory.ts) in `effect-test-sql` is a library example. Its `create` returns the package's `migrationSteps(migrations)` API, its worker Layer provides PostgreSQL, and `around: withIsolatedDatabase` isolates each story. The `oldSchema` and `existingContacts` traits put contacts with email addresses into the old schema; the `backfill` verb calls `steps.migrateToLatest`. Its [story](https://github.com/ShivaeDev/platform/blob/main/packages/effect-test-sql/src/server/migrationStory.spec.ts) proves that those existing addresses survive the migration into a new table. It runs only when `PLATFORM_EFFECT_TEST_SQL_TEST_DATABASE_URL` is configured; a skipped story proves nothing.
376
400
 
377
- A fake belongs only at a true external boundary, wired in `create`; keep the package's own engine real. The [resume kit](https://github.com/ShivaeDev/platform/blob/main/packages/effect-contract/src/test-support/resumeStory.ts), for example, creates synthetic browser and native event sources while running the package's real `resumeSignal` API. Its [stories](https://github.com/ShivaeDev/platform/blob/main/packages/effect-contract/src/resume.dom.test.ts) exercise refresh on those events and listener cleanup. Those synthetic sources prove the package's response to events, not a physical browser or native application's event delivery.
401
+ A fake belongs only at a true external boundary, wired in `create`; keep the package's own engine real. The [resume kit](https://github.com/ShivaeDev/platform/blob/main/packages/effect-contract/src/test-support/resumeStory.ts), for example, creates synthetic browser and native event sources while running the package's real `resumeSignal` API.
378
402
 
379
403
  ### Whole-stack application kits
380
404
 
@@ -382,7 +406,7 @@ Keep one kit in the application’s `test-support/`. `effect-test` owns Vitest r
382
406
 
383
407
  Set `runIf: hasPostgres` in the kit definition to gate all its stories once, where `hasPostgres` is a boolean such as `process.env.PLATFORM_EFFECT_SQL_TEST_DATABASE_URL !== undefined`. When false, `it`, `it.fails`, `it.only`, `it.skip`, `it.runIf` and `it.skipIf` all register skipped stories without acquiring the worker Layer or running `around`, creation, traits or bodies. A skipped `it.only` does not focus the file. When true or omitted, each variant keeps its usual Vitest behavior, including explicit skips and `fails` inverting a failure. Per-story conditions can skip more stories; they cannot enable a disabled kit. The engine, traits, verbs and services remain inferred from the definition.
384
408
 
385
- The [order feature kit](https://github.com/ShivaeDev/platform/tree/main/packages/effect-react/src/test-support/order-example) runs the existing feature’s services, repository, contracts, JSON serialization and authentication middleware over a loopback HTTP listener. Its [stories](https://github.com/ShivaeDev/platform/blob/main/packages/effect-react/src/orderStory.dom.spec.tsx) test visible commits through a separate PostgreSQL pool, rollback of a rejected write without publication, cross-owner rejection, a native React action refreshing a list, and an editor retaining rejected input before a corrected save.
409
+ The [order feature kit](https://github.com/ShivaeDev/platform/tree/main/packages/effect-react/src/test-support/order-example) is an example: it runs the feature’s services, repository, contracts, JSON serialization and authentication middleware over a loopback HTTP listener with bearer tokens.
386
410
 
387
411
  For full-app stories, use `around: withIsolatedDatabase` from `@shivaedev/effect-test-sql/withIsolatedDatabase.ts` with a PostgreSQL test layer prepared from the application's migrations. Each story gets a database cloned from that template, and HTTP and worker pools commit into the same clone. The clone is dropped after scoped hosts and pools close. The starter's [application kit](https://github.com/ShivaeDev/platform/blob/main/examples/starter/src/test-support/application.ts) uses this isolation path.
388
412
 
@@ -390,7 +414,7 @@ For repository-level tests on a migrated template, `withTestTransaction` from `@
390
414
 
391
415
  React screen traits acquire roots in Scope and use a fresh native atom registry for each screen. Cleanup unmounts roots before disposing registries and servers. Screen verbs dispatch DOM events and use bounded observations of rendered output. Select a live clock once on an HTTP/DOM kit; keep TestClock for deterministic local stories. `runUntil` is for engines with a meaningful step hook, not HTTP or DOM polling. Creation forms can use the same pattern with the existing `useCreate` path.
392
416
 
393
- The order example proves DOM and loopback HTTP behavior with bearer tokens. For cookie sessions, use the DOM host setup below. These fixtures do not prove browser networking, SSR, mobile or a deployed proxy. Native `RpcTest` can exercise handlers in faster local stories, but omits serialization and transport. Failure inspection is synchronous: keep redacted observed values in the engine instead of trying to query PostgreSQL inside `inspect`.
417
+ For cookie sessions, use the DOM host setup below. Native `RpcTest` can exercise handlers in faster local stories, but omits serialization and transport. Failure inspection is synchronous: keep redacted observed values in the engine instead of trying to query PostgreSQL inside `inspect`.
394
418
 
395
419
  ### DOM stories with cookie sessions
396
420
 
@@ -439,39 +463,47 @@ const cookieApp = storyKit({
439
463
 
440
464
  Happy-dom's detached window records a location change without loading the next document. `domNavigate(url = window.location.href)` from `@shivaedev/test-story/domNavigate.ts` performs that load: it follows HTTP redirects through the document transport, keeps cookies for each destination, sets the final location, writes the returned HTML and returns the happy-dom response. Call it after the sign-in client has set the provider location, and before checking the returned page. An unrouted external redirect fails with its hostname.
441
465
 
442
- The starter's [social application kit](https://github.com/ShivaeDev/platform/blob/main/examples/starter/src/test-support/socialApp.ts) runs a fixture Better Auth host on real PostgreSQL, with a rendered sign-in button and a fake GitHub provider. Its `outbound` map routes both `github.com` and `api.github.com` to the fake's loopback origin. The fake's authorize endpoint returns the application's callback URL with the received state and a test code. The real callback exchanges that code and fetches the user against the fake, creates the account and session in PostgreSQL, and redirects to the signed-in page.
466
+ The starter's [social application kit](https://github.com/ShivaeDev/platform/blob/main/examples/starter/src/test-support/socialApp.ts) runs a fixture Better Auth host on real PostgreSQL, with a rendered sign-in button and a fake GitHub provider. Its `create` hook acquires the provider and uses scoped `routeOutbound` routing for both `github.com` and `api.github.com`. The fake's authorize endpoint returns the application's callback URL with the received state and a test code. The real callback exchanges that code and fetches the user against the fake, creates the account and session in PostgreSQL, and redirects to the signed-in page.
443
467
 
444
- ```ts
445
- import { domNavigate } from "@shivaedev/test-story/domNavigate.ts";
468
+ The kit renders a Better Auth client whose `customFetchImpl` is `domFetch(baseUrl)`. The story calls `clickGitHubSignIn`, `settle` and `followRedirect` separately, then reads the heading, cookie and session. The screen driver settles on the screen's request state and reads the heading from the current document after navigation. The provider uses `fakeHttpService`. The authorize, token and user requests at the fake and a later session response provide evidence across the provider and application boundaries. The kit does not copy cookies into request headers.
446
469
 
447
- const socialApp = storyKit({
448
- ...socialDefinition,
449
- outbound: { "github.com": fakeUrl, "api.github.com": fakeUrl },
450
- verbs: (engine, story) => ({
451
- signInWithGitHub: () => Effect.promise(async () => {
452
- story.tell("Alice clicks Sign in with GitHub");
453
- await engine.screen.click();
454
- await domNavigate();
455
- }),
456
- signedInPage: () => Effect.promise(async () => ({
457
- heading: document.querySelector("h1")?.textContent,
458
- session: await (await engine.fetch("/api/auth/get-session")).json(),
459
- })),
460
- }),
470
+ ```ts
471
+ socialApp.it("a social sign-in returns with a cookie session", [], function* ({ clickGitHubSignIn, settle, followRedirect, heading, cookie, session }) {
472
+ yield* clickGitHubSignIn();
473
+ yield* settle();
474
+ const response = yield* followRedirect();
475
+ expect(response.status).toBe(200);
476
+ expect(heading()).toBe("Signed in as alice@social.test");
477
+ expect(cookie()).not.toContain("session_token");
478
+ expect(yield* session()).toMatchObject({ user: { email: "alice@social.test" } });
461
479
  });
480
+ ```
462
481
 
463
- socialApp.it("a social sign-in returns with a cookie session", [], function* ({ signInWithGitHub, signedInPage }) {
464
- yield* signInWithGitHub();
465
- expect(yield* signedInPage()).toMatchObject({
466
- heading: "Signed in as alice@social.test",
467
- session: { user: { email: "alice@social.test" } },
468
- });
469
- });
482
+ `domFetch` uses the current window and its cookie jar; it does not isolate multiple users. Run stories using that window serially, and let the application's cleanup end its sessions, restore the page location and close its host. Cookies follow hostnames, not listener ports, so a different loopback port alone does not isolate accounts.
483
+
484
+ ### Driving a screen
485
+
486
+ `screenDriver(options)` supplies DOM verbs and readouts for a kit's `verbs` hook. Give it the mounted `root` (or a getter for a root that navigation replaces), the story's `tell`, and a `pending()` function that reads the application's own request or atom state. `settle()` waits until that signal is false; repeated identical renders never count as completion. The application must keep the signal true until its resulting screen work is ready to render.
487
+
488
+ ```ts
489
+ import { screenDriver } from "@shivaedev/test-story/screenDriver.ts";
490
+
491
+ verbs: ({ view, requests }, story) => screenDriver({
492
+ root: view.container,
493
+ pending: () => requests.pending,
494
+ tell: story.tell,
495
+ })
470
496
  ```
471
497
 
472
- `socialDefinition` starts the host and renders the screen with a Better Auth client whose `customFetchImpl` is `domFetch(baseUrl)`. The authorize, token and user requests at the fake and a later session response provide evidence across the provider and application boundaries. It does not copy cookies into request headers.
498
+ `click(selector)`, `enter(selector, value)` and `submit(selector = "form")` return Effects. Selectors identify exactly one descendant of the root; missing or ambiguous controls fail. Hidden targets and targets inside hidden ancestors refuse actions and readouts. Text entry focuses an input or textarea and dispatches input and change events. Disabled controls refuse clicks and entry, inert ancestors also prevent form submission, and read-only fields refuse entry. Actions do not wait for settlement, so a story can release a held request before calling `settle()`.
473
499
 
474
- `domFetch` uses the current window and its cookie jar; it does not isolate multiple users. Run stories using that window serially, and let the application's cleanup end its sessions, restore the page location and close its host. Cookies follow hostnames, not listener ports, so a different loopback port alone does not isolate accounts.
500
+ `text(selector?)` returns rendered text from the selected element or the whole root. `value(selector)` returns the current input or textarea value. Both narrate what they read through `tell` and leave assertions to the spec. Entry narration omits the supplied value.
501
+
502
+ `accessible(selector)` returns `{ name, role }` computed by `dom-accessibility-api`, including native roles and label references that take precedence over `aria-label`. It narrates the readout and uses the same single rendered target checks as other readouts. Assert on this result when checking a control's accessible name; reading its raw attributes does not compute that name. This DOM computation does not inspect a browser's accessibility tree or prove speech from a screen reader.
503
+
504
+ For React, pass `act: async (action) => { await act(action); }` using React's `act`. The adapter wraps each action and flushes renderer work before checking the pending signal, including while waiting for an asynchronous response. The [React driver stories](https://github.com/ShivaeDev/platform/blob/main/packages/effect-react/src/screenDriver.dom.spec.tsx) cover a native pending atom, a rendered response and a controlled field without React warnings. The driver itself has no renderer dependency.
505
+
506
+ Settlement has a live-time limit of 3 seconds, configurable with the driver's `timeoutMs` or per call with `settle({ timeoutMs })`, and fails with the current screen text and advice when that limit expires. Use the existing DOM environment and scoped `routeOutbound` routing to reach `fakeHttpService`; the driver does not install another transport. The [screen stories](src/server/screenSignals.dom.spec.ts) hold a real HTTP response across repeated renders and sign in by filling and submitting a form against a fake identity provider. These fixtures prove DOM interactions and loopback HTTP, not a browser or a production authentication system.
475
507
 
476
508
  ### API by module
477
509
 
@@ -481,6 +513,8 @@ Import each module with the prefix `@shivaedev/test-story/`; there is no root en
481
513
  | --- | --- |
482
514
  | `storyKit.ts` | `storyKit`, `Trait`, `RunUntilOptions`, `Story`, `RunHooks`, `StoryKitDefinition`, `Told`, `StoryBody`, `StoryTest`, `StoryIt`, `StoryKit` |
483
515
  | `domFetch.ts` | `domFetch(baseUrl)` |
516
+ | `routeOutbound.ts` | `routeOutbound(routes)` |
517
+ | `screenDriver.ts` | `screenDriver(options)`, `ScreenDriverOptions`, `ScreenDriver` |
484
518
  | `domNavigate.ts` | `domNavigate(url?)` |
485
519
  | `genreTags.ts` | `genreTags(...kitNames)`, `GenreTag` |
486
520
  | `domEnvironment.config.ts` | Default Vitest environment preserving Node host globals and happy-dom document transport |
@@ -496,7 +530,7 @@ pnpm add --save-dev @shivaedev/test-story @effect/vitest@4.0.4 effect@4.0.4 vite
496
530
  `@effect/vitest`, `effect` and `vitest` are peers, so the application's own versions run the stories.
497
531
 
498
532
  - The package runs inside Vitest. The story reaches the failure by rewriting the first error's message in Vitest's `onTestFailed` hook.
499
- - Layer acquisition and engine creation failures precede that hook and carry no story appendix. A printed rerun command still needs a Vitest configuration that collects its file; the intentional `failingStories.ts` examples use the package's explicit fixture runner.
533
+ - Layer acquisition and engine creation failures precede that hook and carry no story appendix. A printed rerun command still needs a Vitest configuration that collects its file.
500
534
  - A kit's `it` is created when the kit is defined, so a kit lives in a module that only Vitest test files import.
501
535
  - `inspect` runs after the test has failed, so it reads the engine synchronously.
502
536
  - The spec line under the `✗` is found by matching the spec's path in the failure's stack, or else in the call site of the last line the story told or the last `runUntil`. A failure raised inside an Effect that the spec does not call directly may have none.
@@ -7,7 +7,7 @@ const domEnvironment = {
7
7
  // Vitest loads environments outside the project's source resolution conditions.
8
8
  const load = createRequire(import.meta.url);
9
9
  const { documentTransport } = load("#internal/documentTransport.ts");
10
- const { installOutbound } = load("#internal/routeOutbound.ts");
10
+ const { installOutbound } = load("#internal/installOutbound.ts");
11
11
  const native = Object.fromEntries(["fetch", "AbortController", "AbortSignal", "Headers", "Request", "Response"].map((name) => [name, Reflect.get(global, name)]));
12
12
  const restoreOutbound = await installOutbound({}, true);
13
13
  const win = new GlobalWindow({
@@ -1 +1 @@
1
- {"version":3,"file":"domEnvironment.config.js","sourceRoot":"","sources":["../../src/server/domEnvironment.config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"domEnvironment.config.js","sourceRoot":"","sources":["../../src/server/domEnvironment.config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function installOutbound(routes: Readonly<Record<string, string>>, strict?: boolean): Promise<() => void>;
2
+ //# sourceMappingURL=installOutbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installOutbound.d.ts","sourceRoot":"","sources":["../../../src/server/internal/installOutbound.ts"],"names":[],"mappings":"AAeA,wBAAsB,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,UAAQ,uBAgD7F"}
@@ -1,5 +1,4 @@
1
1
  import { isIP } from "node:net";
2
- import { Effect } from "effect";
3
2
  import { unrouted } from "#internal/errors.ts";
4
3
  function isDispatcher(value) {
5
4
  return typeof value === "object" && value !== null && "dispatch" in value && typeof value.dispatch === "function";
@@ -52,7 +51,4 @@ export async function installOutbound(routes, strict = false) {
52
51
  }
53
52
  };
54
53
  }
55
- export function routeOutbound(routes) {
56
- return Effect.acquireRelease(Effect.promise(() => installOutbound(routes, Reflect.get(globalThis, Symbol.for("test-story.dom")) === true)), (restore) => Effect.sync(restore)).pipe(Effect.asVoid);
57
- }
58
- //# sourceMappingURL=routeOutbound.js.map
54
+ //# sourceMappingURL=installOutbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installOutbound.js","sourceRoot":"","sources":["../../../src/server/internal/installOutbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare function screenElements(root: () => HTMLElement): {
2
+ element: (selector: string) => HTMLElement;
3
+ enabled: (found: HTMLElement, selector: string) => void;
4
+ enter: (selector: string, value: string) => void;
5
+ form: (selector: string) => HTMLFormElement;
6
+ input: (selector: string) => HTMLInputElement | HTMLTextAreaElement;
7
+ rendered: (found: HTMLElement, selector: string) => HTMLElement;
8
+ };
9
+ //# sourceMappingURL=screenElements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenElements.d.ts","sourceRoot":"","sources":["../../../src/server/internal/screenElements.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,WAAW;wBAa1B,MAAM;qBAiBT,WAAW,YAAY,MAAM;sBAQlC,MAAM,SAAS,MAAM;qBAYtB,MAAM;sBA3BC,MAAM;sBAtBN,WAAW,YAAY,MAAM;EA4DtD"}
@@ -0,0 +1,61 @@
1
+ export function screenElements(root) {
2
+ function rendered(found, selector) {
3
+ const visibility = getComputedStyle(found).visibility;
4
+ let hidden = visibility === "hidden" || visibility === "collapse";
5
+ for (let ancestor = found; ancestor; ancestor = ancestor.parentElement) {
6
+ const style = getComputedStyle(ancestor);
7
+ hidden ||= Boolean(ancestor.hidden) || style.display === "none" || style.contentVisibility === "hidden";
8
+ }
9
+ if (hidden) {
10
+ throw new Error(`The screen's ${selector} is not rendered.\nhelp: Reveal the target and its ancestors before acting on or reading it.`);
11
+ }
12
+ return found;
13
+ }
14
+ function element(selector) {
15
+ const matches = root().querySelectorAll(selector);
16
+ const found = matches[0];
17
+ if (matches.length !== 1 || found === undefined) {
18
+ throw new Error(`The screen has ${matches.length} matches for ${JSON.stringify(selector)}.\nhelp: Use a selector for exactly one rendered control or readout.`);
19
+ }
20
+ return rendered(found, selector);
21
+ }
22
+ function input(selector) {
23
+ const found = element(selector);
24
+ if (!(found instanceof HTMLInputElement || found instanceof HTMLTextAreaElement)) {
25
+ throw new Error(`The screen's ${selector} is not a text field.\nhelp: Select a rendered input or textarea to enter or read a value.`);
26
+ }
27
+ return found;
28
+ }
29
+ function enabled(found, selector) {
30
+ if (found.matches(":disabled") || found.closest("[inert]")) {
31
+ throw new Error(`The screen's ${selector} is disabled.\nhelp: Wait for the app to enable the control before acting on it.`);
32
+ }
33
+ }
34
+ return {
35
+ element,
36
+ enabled,
37
+ enter: (selector, value) => {
38
+ const field = input(selector);
39
+ enabled(field, selector);
40
+ if (field.readOnly) {
41
+ throw new Error(`The screen's ${selector} is read-only.\nhelp: Select an editable input or textarea.`);
42
+ }
43
+ field.focus();
44
+ const prototype = field instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
45
+ Object.getOwnPropertyDescriptor(prototype, "value")?.set?.call(field, value);
46
+ field.dispatchEvent(new Event("input", { bubbles: true }));
47
+ field.dispatchEvent(new Event("change", { bubbles: true }));
48
+ },
49
+ form: (selector) => {
50
+ const found = element(selector);
51
+ if (!(found instanceof HTMLFormElement)) {
52
+ throw new Error(`The screen's ${selector} is not a form.\nhelp: Select a rendered form to submit.`);
53
+ }
54
+ enabled(found, selector);
55
+ return found;
56
+ },
57
+ input,
58
+ rendered,
59
+ };
60
+ }
61
+ //# sourceMappingURL=screenElements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenElements.js","sourceRoot":"","sources":["../../../src/server/internal/screenElements.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Effect, type Scope } from "effect";
2
- import type { AnyTestLayer } from "@shivaedev/effect-test/any-test-layer.ts";
2
+ import type { AnyTestLayer } from "@shivaedev/effect-test/anyTestLayer.ts";
3
3
  import type { EffectClock } from "@shivaedev/effect-test/types.ts";
4
4
  import { type AnyEffect, type EffectHook, type Hook } from "#internal/hooked.ts";
5
5
  import { type Narration } from "#internal/narration.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/server/internal/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAAE,KAAK,KAAK,EAAU,MAAM,QAAQ,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,EAAU,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnG,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5I,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1G,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;CAC/D;AAED,eAAO,MAAM,IAAI,6IAuBf,CAAC"}
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/server/internal/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAAE,KAAK,KAAK,EAAU,MAAM,QAAQ,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,EAAU,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnG,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5I,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1G,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;CAC/D;AAED,eAAO,MAAM,IAAI,6IAuBf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/server/internal/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAK1C,OAAO,EAAE,KAAK,eAAe,EAAmB,KAAK,UAAU,EAAQ,MAAM,mBAAmB,CAAC;AAEjG,eAAO,MAAM,KAAK,iGAIJ,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,6CAkB/D,CAAC"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/server/internal/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAK1C,OAAO,EAAE,KAAK,eAAe,EAAmB,KAAK,UAAU,EAAQ,MAAM,mBAAmB,CAAC;AAEjG,eAAO,MAAM,KAAK,iGAIJ,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,6CA0B/D,CAAC"}
@@ -19,7 +19,10 @@ export const start = Effect.fnUntraced(function* (definition, given, context, bo
19
19
  },
20
20
  tell: narration.tell,
21
21
  };
22
- return yield* body({ ...definition.verbs(engine, story), story });
22
+ return yield* body(Object.defineProperties({}, {
23
+ ...Object.getOwnPropertyDescriptors(definition.verbs(engine, story)),
24
+ story: { enumerable: true, value: story },
25
+ }));
23
26
  }).pipe(Effect.tapCause((cause) => Effect.sync(() => captureFailure(Cause.prettyErrors(cause)[0]?.stack))));
24
27
  });
25
28
  //# sourceMappingURL=start.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/server/internal/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAA0D,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEjG,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC/C,UAA2B,EAC3B,KAA4B,EAC5B,OAAoB,EACpB,IAAgE;IAEhE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;IACvH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,KAAK,GAAe;YACzB,MAAM;YACN,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC5B,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACxB,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;gBACpB,OAAO,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACvI,CAAC;YACD,IAAI,EAAE,SAAS,CAAC,IAAI;SACpB,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7G,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/server/internal/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAA0D,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEjG,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC/C,UAA2B,EAC3B,KAA4B,EAC5B,OAAoB,EACpB,IAAgE;IAEhE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;IACvH,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,KAAK,GAAe;YACzB,MAAM;YACN,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC5B,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACxB,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;gBACpB,OAAO,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACvI,CAAC;YACD,IAAI,EAAE,SAAS,CAAC,IAAI;SACpB,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,IAAI,CACjB,MAAM,CAAC,gBAAgB,CACtB,EAAE,EACF;YACC,GAAG,MAAM,CAAC,yBAAyB,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACpE,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;SACzC,CACD,CACD,CAAC;IACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7G,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"storyIt.d.ts","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGrF,OAAO,EAAE,KAAK,SAAS,EAAU,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGrE,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;AAE1G,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,KAAK,IAAI,CAAC;AAE9H,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC;CACnD;AAgCD,wBAAgB,OAAO,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CA0B5D"}
1
+ {"version":3,"file":"storyIt.d.ts","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGrF,OAAO,EAAE,KAAK,SAAS,EAAU,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIrE,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;AAE1G,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,KAAK,IAAI,CAAC;AAE9H,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,CAAC;CACnD;AAgCD,wBAAgB,OAAO,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CA0B5D"}
@@ -2,8 +2,8 @@ import { Effect, Layer } from "effect";
2
2
  import { makeEffectIt } from "@shivaedev/effect-test/vitest.ts";
3
3
  import { genreOf } from "#internal/genre.ts";
4
4
  import { hooked } from "#internal/hooked.ts";
5
- import { routeOutbound } from "#internal/routeOutbound.ts";
6
5
  import { start } from "#internal/start.ts";
6
+ import { routeOutbound } from "#routeOutbound.ts";
7
7
  function withTags(options, tags) {
8
8
  if (typeof options === "number") {
9
9
  return { tags: [...tags], timeout: options };
@@ -1 +1 @@
1
- {"version":3,"file":"storyIt.js","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storyIt.js","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
1
  import { Effect } from "effect";
2
- export declare function installOutbound(routes: Readonly<Record<string, string>>, strict?: boolean): Promise<() => void>;
3
2
  export declare function routeOutbound(routes: Readonly<Record<string, string>>): Effect.Effect<void, never, import("effect/Scope").Scope>;
4
3
  //# sourceMappingURL=routeOutbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeOutbound.d.ts","sourceRoot":"","sources":["../../src/server/routeOutbound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,4DAKrE"}
@@ -0,0 +1,6 @@
1
+ import { Effect } from "effect";
2
+ import { installOutbound } from "#internal/installOutbound.ts";
3
+ export function routeOutbound(routes) {
4
+ return Effect.acquireRelease(Effect.promise(() => installOutbound(routes, Reflect.get(globalThis, Symbol.for("test-story.dom")) === true)), (restore) => Effect.sync(restore)).pipe(Effect.asVoid);
5
+ }
6
+ //# sourceMappingURL=routeOutbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeOutbound.js","sourceRoot":"","sources":["../../src/server/routeOutbound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,MAAM,UAAU,aAAa,CAAC,MAAwC;IACrE,OAAO,MAAM,CAAC,cAAc,CAC3B,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EAC7G,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CACjC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Effect } from "effect";
2
+ export interface ScreenDriverOptions {
3
+ readonly act?: (action: () => Promise<void>) => PromiseLike<void>;
4
+ readonly pending: () => boolean;
5
+ readonly root: HTMLElement | (() => HTMLElement);
6
+ readonly tell: (line: string) => void;
7
+ readonly timeoutMs?: number;
8
+ }
9
+ export type ScreenDriver = ReturnType<typeof screenDriver>;
10
+ export declare function screenDriver({ root, pending, tell, act, timeoutMs }: ScreenDriverOptions): {
11
+ accessible: (selector: string) => {
12
+ name: string;
13
+ role: string | null;
14
+ };
15
+ click: (selector: string) => Effect.Effect<void, never, never>;
16
+ enter: (selector: string, value: string) => Effect.Effect<void, never, never>;
17
+ settle: (options?: {
18
+ readonly timeoutMs?: number;
19
+ }) => Effect.Effect<void, never, never>;
20
+ submit: (selector?: string) => Effect.Effect<void, never, never>;
21
+ text: (selector?: string) => string;
22
+ value: (selector: string) => string;
23
+ };
24
+ //# sourceMappingURL=screenDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenDriver.d.ts","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3D,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAA0B,EAAE,SAAgB,EAAE,EAAE,mBAAmB;2BAgC9F,MAAM;;;;sBAMX,MAAM;sBAON,MAAM,SAAS,MAAM;uBApCf;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;;sBAuCrC,MAAM;sBAKN,MAAM;EAMzB"}
@@ -0,0 +1,60 @@
1
+ import { computeAccessibleName, getRole } from "dom-accessibility-api";
2
+ import { Effect } from "effect";
3
+ import { withLive } from "@shivaedev/effect-test/withLive.ts";
4
+ import { screenElements } from "#internal/screenElements.ts";
5
+ export function screenDriver({ root, pending, tell, act = (action) => action(), timeoutMs = 3000 }) {
6
+ const currentRoot = typeof root === "function" ? root : () => root;
7
+ const elements = screenElements(currentRoot);
8
+ function perform(line, action) {
9
+ return Effect.promise(async () => {
10
+ tell(line);
11
+ await act(async () => action());
12
+ });
13
+ }
14
+ function settle(options = {}) {
15
+ const limit = options.timeoutMs ?? timeoutMs;
16
+ const wait = Effect.gen(function* () {
17
+ tell("wait for the screen's pending work");
18
+ do {
19
+ yield* Effect.promise(() => act(async () => undefined));
20
+ if (!pending()) {
21
+ tell("the screen has no pending work");
22
+ return;
23
+ }
24
+ yield* Effect.promise(() => act(() => new Promise((resolve) => setTimeout(resolve, 10))));
25
+ } while (true);
26
+ });
27
+ function timedOut() {
28
+ return Effect.die(new Error(`The screen did not settle within ${limit}ms.\nhelp: Release held requests and check the app's pending signal and renderer act adapter.\nScreen: ${currentRoot().textContent}`));
29
+ }
30
+ return withLive(wait.pipe(Effect.timeoutOrElse({ duration: limit, orElse: timedOut })));
31
+ }
32
+ return {
33
+ accessible: (selector) => {
34
+ const element = elements.element(selector);
35
+ const accessible = { name: computeAccessibleName(element), role: getRole(element) };
36
+ tell(`read the accessibility of ${selector}: ${JSON.stringify(accessible)}`);
37
+ return accessible;
38
+ },
39
+ click: (selector) => perform(`click ${selector}`, () => {
40
+ const element = elements.element(selector);
41
+ elements.enabled(element, selector);
42
+ element.focus();
43
+ element.click();
44
+ }),
45
+ enter: (selector, value) => perform(`enter a value in ${selector}`, () => elements.enter(selector, value)),
46
+ settle,
47
+ submit: (selector = "form") => perform(`submit ${selector}`, () => elements.form(selector).requestSubmit()),
48
+ text: (selector) => {
49
+ const text = (selector === undefined ? elements.rendered(currentRoot(), "root") : elements.element(selector)).innerText;
50
+ tell(`read ${selector ?? "the screen"}: ${JSON.stringify(text)}`);
51
+ return text;
52
+ },
53
+ value: (selector) => {
54
+ const value = elements.input(selector).value;
55
+ tell(`read the value in ${selector}: ${JSON.stringify(value)}`);
56
+ return value;
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=screenDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenDriver.js","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"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"}
@@ -37,7 +37,7 @@ export interface StoryKitDefinition<TEngine, TStage extends string, TVerbs, TPro
37
37
  readonly outbound?: Readonly<Record<string, string>>;
38
38
  readonly run?: RunHooks<TEngine, TStep, TFailed, TDiagnose>;
39
39
  readonly runIf?: boolean;
40
- readonly stages: readonly [TStage, ...TStage[]];
40
+ readonly stages: readonly TStage[];
41
41
  readonly verbs: (engine: TEngine, story: Story<TEngine, TStep | TFailed | TDiagnose>) => TVerbs;
42
42
  }
43
43
  export type Told<TEngine, TVerbs, TRun extends AnyEffect> = TVerbs & {
@@ -60,8 +60,8 @@ export interface StoryKit<TEngine, TStage extends string, TVerbs, TRun extends A
60
60
  };
61
61
  readonly traits: <const TGiven extends readonly Trait<TEngine, TStage, unknown>[]>(...given: TGiven) => Trait<TEngine, TStage, TraitServices<TGiven[number]>>;
62
62
  }
63
- export declare function storyKit<TCreate extends () => unknown, const TStage extends string, TVerbs extends object, TProvided = never, TAfter extends AfterResults<TStage> = Record<never, never>, TStep extends AnyEffect = never, TFailed extends AnyEffect = never, TDiagnose extends AnyEffect = never>(definition: InferredDefinition<TCreate, TStage, TVerbs, TProvided, TAfter, TStep, TFailed, TDiagnose>): StoryKit<HookValue<ReturnType<TCreate>>, TStage, TVerbs, TStep | TFailed | TDiagnose, TProvided>;
64
- export declare function storyKit<TEngine, const TStage extends string, TVerbs extends object, TProvided = never, TCreate extends AnyEffect = never, TAfter extends {
63
+ export declare function storyKit<TCreate extends () => unknown, const TStage extends string = never, TVerbs extends object = object, TProvided = never, TAfter extends AfterResults<TStage> = Record<never, never>, TStep extends AnyEffect = never, TFailed extends AnyEffect = never, TDiagnose extends AnyEffect = never>(definition: InferredDefinition<TCreate, TStage, TVerbs, TProvided, TAfter, TStep, TFailed, TDiagnose>): StoryKit<HookValue<ReturnType<TCreate>>, TStage, TVerbs, TStep | TFailed | TDiagnose, TProvided>;
64
+ export declare function storyKit<TEngine, const TStage extends string = never, TVerbs extends object = object, TProvided = never, TCreate extends AnyEffect = never, TAfter extends {
65
65
  readonly [TKey in TStage]?: AnyEffect;
66
66
  } = Record<never, never>, TStep extends AnyEffect = never, TFailed extends AnyEffect = never, TDiagnose extends AnyEffect = never>(definition: LegacyDefinition<TEngine, TStage, TVerbs, TProvided, TCreate, TAfter, TStep, TFailed, TDiagnose>): StoryKit<TEngine, TStage, TVerbs, TStep | TFailed | TDiagnose, TProvided>;
67
67
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"storyKit.d.ts","sourceRoot":"","sources":["../../src/server/storyKit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storyKit.d.ts","sourceRoot":"","sources":["../../src/server/storyKit.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/test-story",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Tests that read like stories over the real engine: a story kit per engine with its own it, traits per feature, and a failure that prints the story, where it stopped and the engine's state",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme",
6
6
  "bugs": {
@@ -24,9 +24,8 @@
24
24
  },
25
25
  "exports": {
26
26
  "./*.ts": {
27
- "types": "./dist/server/*.d.ts",
28
27
  "source": "./src/server/*.ts",
29
- "import": "./dist/server/*.js",
28
+ "types": "./dist/server/*.d.ts",
30
29
  "default": "./dist/server/*.js"
31
30
  },
32
31
  "./internal/*": null,
@@ -38,21 +37,20 @@
38
37
  "!src/**/*.test.ts",
39
38
  "!src/**/*.spec.ts",
40
39
  "!src/**/test-support",
41
- "docs",
42
40
  "CHANGELOG.md",
43
41
  "README.md"
44
42
  ],
45
43
  "dependencies": {
46
- "@shivaedev/effect-test": "0.5.0"
44
+ "@shivaedev/effect-test": "0.6.0",
45
+ "dom-accessibility-api": "0.7.1"
47
46
  },
48
47
  "devDependencies": {
49
48
  "@effect/vitest": "4.0.4",
50
- "@shivaedev/quality": "0.24.0",
49
+ "@shivaedev/quality": "0.26.0",
51
50
  "@types/node": "24.19.2",
52
- "@typescript/native": "npm:typescript@7.0.2",
53
51
  "effect": "4.0.4",
54
52
  "happy-dom": "20.14.6",
55
- "typescript": "npm:@typescript/typescript6@6.0.2",
53
+ "typescript": "7.0.2",
56
54
  "vitest": "5.0.3"
57
55
  },
58
56
  "peerDependencies": {
@@ -8,7 +8,7 @@ const domEnvironment: Environment = {
8
8
  // Vitest loads environments outside the project's source resolution conditions.
9
9
  const load = createRequire(import.meta.url);
10
10
  const { documentTransport }: typeof import("#internal/documentTransport.ts") = load("#internal/documentTransport.ts");
11
- const { installOutbound }: typeof import("#internal/routeOutbound.ts") = load("#internal/routeOutbound.ts");
11
+ const { installOutbound }: typeof import("#internal/installOutbound.ts") = load("#internal/installOutbound.ts");
12
12
  const native = Object.fromEntries(
13
13
  ["fetch", "AbortController", "AbortSignal", "Headers", "Request", "Response"].map((name) => [name, Reflect.get(global, name)]),
14
14
  );
@@ -1,5 +1,4 @@
1
1
  import { isIP } from "node:net";
2
- import { Effect } from "effect";
3
2
  import { unrouted } from "#internal/errors.ts";
4
3
 
5
4
  interface DispatchOptions {
@@ -63,10 +62,3 @@ export async function installOutbound(routes: Readonly<Record<string, string>>,
63
62
  }
64
63
  };
65
64
  }
66
-
67
- export function routeOutbound(routes: Readonly<Record<string, string>>) {
68
- return Effect.acquireRelease(
69
- Effect.promise(() => installOutbound(routes, Reflect.get(globalThis, Symbol.for("test-story.dom")) === true)),
70
- (restore) => Effect.sync(restore),
71
- ).pipe(Effect.asVoid);
72
- }
@@ -0,0 +1,62 @@
1
+ export function screenElements(root: () => HTMLElement) {
2
+ function rendered(found: HTMLElement, selector: string) {
3
+ const visibility = getComputedStyle(found).visibility;
4
+ let hidden = visibility === "hidden" || visibility === "collapse";
5
+ for (let ancestor: HTMLElement | null = found; ancestor; ancestor = ancestor.parentElement) {
6
+ const style = getComputedStyle(ancestor);
7
+ hidden ||= Boolean(ancestor.hidden) || style.display === "none" || style.contentVisibility === "hidden";
8
+ }
9
+ if (hidden) {
10
+ throw new Error(`The screen's ${selector} is not rendered.\nhelp: Reveal the target and its ancestors before acting on or reading it.`);
11
+ }
12
+ return found;
13
+ }
14
+ function element(selector: string) {
15
+ const matches = root().querySelectorAll<HTMLElement>(selector);
16
+ const found = matches[0];
17
+ if (matches.length !== 1 || found === undefined) {
18
+ throw new Error(
19
+ `The screen has ${matches.length} matches for ${JSON.stringify(selector)}.\nhelp: Use a selector for exactly one rendered control or readout.`,
20
+ );
21
+ }
22
+ return rendered(found, selector);
23
+ }
24
+ function input(selector: string) {
25
+ const found = element(selector);
26
+ if (!(found instanceof HTMLInputElement || found instanceof HTMLTextAreaElement)) {
27
+ throw new Error(`The screen's ${selector} is not a text field.\nhelp: Select a rendered input or textarea to enter or read a value.`);
28
+ }
29
+ return found;
30
+ }
31
+ function enabled(found: HTMLElement, selector: string) {
32
+ if (found.matches(":disabled") || found.closest("[inert]")) {
33
+ throw new Error(`The screen's ${selector} is disabled.\nhelp: Wait for the app to enable the control before acting on it.`);
34
+ }
35
+ }
36
+ return {
37
+ element,
38
+ enabled,
39
+ enter: (selector: string, value: string) => {
40
+ const field = input(selector);
41
+ enabled(field, selector);
42
+ if (field.readOnly) {
43
+ throw new Error(`The screen's ${selector} is read-only.\nhelp: Select an editable input or textarea.`);
44
+ }
45
+ field.focus();
46
+ const prototype = field instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
47
+ Object.getOwnPropertyDescriptor(prototype, "value")?.set?.call(field, value);
48
+ field.dispatchEvent(new Event("input", { bubbles: true }));
49
+ field.dispatchEvent(new Event("change", { bubbles: true }));
50
+ },
51
+ form: (selector: string) => {
52
+ const found = element(selector);
53
+ if (!(found instanceof HTMLFormElement)) {
54
+ throw new Error(`The screen's ${selector} is not a form.\nhelp: Select a rendered form to submit.`);
55
+ }
56
+ enabled(found, selector);
57
+ return found;
58
+ },
59
+ input,
60
+ rendered,
61
+ };
62
+ }
@@ -1,5 +1,5 @@
1
1
  import { Cause, Context, Effect, type Scope, Tracer } from "effect";
2
- import type { AnyTestLayer } from "@shivaedev/effect-test/any-test-layer.ts";
2
+ import type { AnyTestLayer } from "@shivaedev/effect-test/anyTestLayer.ts";
3
3
  import type { EffectClock } from "@shivaedev/effect-test/types.ts";
4
4
  import { refusal } from "#internal/errors.ts";
5
5
  import { type AnyEffect, type EffectHook, type Hook, hooked } from "#internal/hooked.ts";
@@ -26,6 +26,14 @@ export const start = Effect.fnUntraced(function* (
26
26
  },
27
27
  tell: narration.tell,
28
28
  };
29
- return yield* body({ ...definition.verbs(engine, story), story });
29
+ return yield* body(
30
+ Object.defineProperties(
31
+ {},
32
+ {
33
+ ...Object.getOwnPropertyDescriptors(definition.verbs(engine, story)),
34
+ story: { enumerable: true, value: story },
35
+ },
36
+ ),
37
+ );
30
38
  }).pipe(Effect.tapCause((cause) => Effect.sync(() => captureFailure(Cause.prettyErrors(cause)[0]?.stack))));
31
39
  });
@@ -4,9 +4,9 @@ import type { EffectTest, EffectTestOptions } from "@shivaedev/effect-test/types
4
4
  import { makeEffectIt } from "@shivaedev/effect-test/vitest.ts";
5
5
  import { type Genre, genreOf } from "#internal/genre.ts";
6
6
  import { type AnyEffect, hooked } from "#internal/hooked.ts";
7
- import { routeOutbound } from "#internal/routeOutbound.ts";
8
7
  import type { LooseDefinition, LooseTrait } from "#internal/seed.ts";
9
8
  import { start } from "#internal/start.ts";
9
+ import { routeOutbound } from "#routeOutbound.ts";
10
10
 
11
11
  type LooseBody = (told: object, context: TestContext) => Generator<AnyEffect, unknown, never> | undefined;
12
12
 
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ import { installOutbound } from "#internal/installOutbound.ts";
3
+
4
+ export function routeOutbound(routes: Readonly<Record<string, string>>) {
5
+ return Effect.acquireRelease(
6
+ Effect.promise(() => installOutbound(routes, Reflect.get(globalThis, Symbol.for("test-story.dom")) === true)),
7
+ (restore) => Effect.sync(restore),
8
+ ).pipe(Effect.asVoid);
9
+ }
@@ -0,0 +1,75 @@
1
+ import { computeAccessibleName, getRole } from "dom-accessibility-api";
2
+ import { Effect } from "effect";
3
+ import { withLive } from "@shivaedev/effect-test/withLive.ts";
4
+ import { screenElements } from "#internal/screenElements.ts";
5
+
6
+ export interface ScreenDriverOptions {
7
+ readonly act?: (action: () => Promise<void>) => PromiseLike<void>;
8
+ readonly pending: () => boolean;
9
+ readonly root: HTMLElement | (() => HTMLElement);
10
+ readonly tell: (line: string) => void;
11
+ readonly timeoutMs?: number;
12
+ }
13
+
14
+ export type ScreenDriver = ReturnType<typeof screenDriver>;
15
+
16
+ export function screenDriver({ root, pending, tell, act = (action) => action(), timeoutMs = 3000 }: ScreenDriverOptions) {
17
+ const currentRoot = typeof root === "function" ? root : () => root;
18
+ const elements = screenElements(currentRoot);
19
+ function perform(line: string, action: () => void) {
20
+ return Effect.promise(async () => {
21
+ tell(line);
22
+ await act(async () => action());
23
+ });
24
+ }
25
+ function settle(options: { readonly timeoutMs?: number } = {}) {
26
+ const limit = options.timeoutMs ?? timeoutMs;
27
+ const wait = Effect.gen(function* () {
28
+ tell("wait for the screen's pending work");
29
+ do {
30
+ yield* Effect.promise(() => act(async () => undefined));
31
+ if (!pending()) {
32
+ tell("the screen has no pending work");
33
+ return;
34
+ }
35
+ yield* Effect.promise(() => act(() => new Promise<void>((resolve) => setTimeout(resolve, 10))));
36
+ } while (true);
37
+ });
38
+ function timedOut() {
39
+ return Effect.die(
40
+ new Error(
41
+ `The screen did not settle within ${limit}ms.\nhelp: Release held requests and check the app's pending signal and renderer act adapter.\nScreen: ${currentRoot().textContent}`,
42
+ ),
43
+ );
44
+ }
45
+ return withLive(wait.pipe(Effect.timeoutOrElse({ duration: limit, orElse: timedOut })));
46
+ }
47
+ return {
48
+ accessible: (selector: string) => {
49
+ const element = elements.element(selector);
50
+ const accessible = { name: computeAccessibleName(element), role: getRole(element) };
51
+ tell(`read the accessibility of ${selector}: ${JSON.stringify(accessible)}`);
52
+ return accessible;
53
+ },
54
+ click: (selector: string) =>
55
+ perform(`click ${selector}`, () => {
56
+ const element = elements.element(selector);
57
+ elements.enabled(element, selector);
58
+ element.focus();
59
+ element.click();
60
+ }),
61
+ enter: (selector: string, value: string) => perform(`enter a value in ${selector}`, () => elements.enter(selector, value)),
62
+ settle,
63
+ submit: (selector = "form") => perform(`submit ${selector}`, () => elements.form(selector).requestSubmit()),
64
+ text: (selector?: string) => {
65
+ const text = (selector === undefined ? elements.rendered(currentRoot(), "root") : elements.element(selector)).innerText;
66
+ tell(`read ${selector ?? "the screen"}: ${JSON.stringify(text)}`);
67
+ return text;
68
+ },
69
+ value: (selector: string) => {
70
+ const value = elements.input(selector).value;
71
+ tell(`read the value in ${selector}: ${JSON.stringify(value)}`);
72
+ return value;
73
+ },
74
+ };
75
+ }
@@ -55,7 +55,7 @@ export interface StoryKitDefinition<
55
55
  readonly outbound?: Readonly<Record<string, string>>;
56
56
  readonly run?: RunHooks<TEngine, TStep, TFailed, TDiagnose>;
57
57
  readonly runIf?: boolean;
58
- readonly stages: readonly [TStage, ...TStage[]];
58
+ readonly stages: readonly TStage[];
59
59
  readonly verbs: (engine: TEngine, story: Story<TEngine, TStep | TFailed | TDiagnose>) => TVerbs;
60
60
  }
61
61
 
@@ -101,8 +101,8 @@ export interface StoryKit<TEngine, TStage extends string, TVerbs, TRun extends A
101
101
 
102
102
  export function storyKit<
103
103
  TCreate extends () => unknown,
104
- const TStage extends string,
105
- TVerbs extends object,
104
+ const TStage extends string = never,
105
+ TVerbs extends object = object,
106
106
  TProvided = never,
107
107
  TAfter extends AfterResults<TStage> = Record<never, never>,
108
108
  TStep extends AnyEffect = never,
@@ -114,8 +114,8 @@ export function storyKit<
114
114
  // Keep explicit engine and effect type arguments accepted by existing kits.
115
115
  export function storyKit<
116
116
  TEngine,
117
- const TStage extends string,
118
- TVerbs extends object,
117
+ const TStage extends string = never,
118
+ TVerbs extends object = object,
119
119
  TProvided = never,
120
120
  TCreate extends AnyEffect = never,
121
121
  TAfter extends { readonly [TKey in TStage]?: AnyEffect } = Record<never, never>,
@@ -1 +0,0 @@
1
- {"version":3,"file":"routeOutbound.d.ts","sourceRoot":"","sources":["../../../src/server/internal/routeOutbound.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAehC,wBAAsB,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,UAAQ,uBAgD7F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,4DAKrE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"routeOutbound.js","sourceRoot":"","sources":["../../../src/server/internal/routeOutbound.ts"],"names":[],"mappings":"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"}
@@ -1,35 +0,0 @@
1
- # North star
2
-
3
- ## The problem
4
-
5
- Agents now write most of the code, tests included, and a test file an agent writes tends to be hundreds of lines long. Each test rebuilds its setup by hand, a little differently from the last, and where rebuilding a complex setup is too much work, it mocks the part that was hard to build. Nobody reads these files. A human could ask an agent what a suite actually tests, but nobody asks consistently enough for that to be a safety net, so we lose sight of what we test exactly when more code is written than ever.
6
-
7
- ## The ideal
8
-
9
- A test reads like a short English story: what exists, what someone does, and what is true afterwards. Its setup is a list of traits in the domain's words, such as `ovenIsLit()` and `hasDough(3)`, its actions go through the application's real verbs, and the real engine runs between them. Everything that is not specific to this one test lives elsewhere, written once:
10
-
11
- - once per engine, a story kit that knows how to create the engine, seed it stage by stage, step it and show its state;
12
- - once per feature, the traits and verbs that name that feature in domain words;
13
- - in each test, only the story.
14
-
15
- This changes the cost and benefit of a good test for both readers. For an agent, a story test is the cheaper one to write and to read on every later visit, because it is a handful of lines that reuse what already exists. Because every story runs the whole engine, each test also checks, almost by accident, the parts its author never thought about and would have mocked. For a human, a spec that reads as English leaves no excuse not to read it. With so little noise around the signal, an agent that writes a sloppy or pointless test produces something a reviewer sees at a glance.
16
-
17
- ## What good looks like
18
-
19
- - A spec is a few lines of domain words. If a spec needs a helper, a loop or hand-built state, a trait or verb is missing from the kit.
20
- - An impossible setup refuses loudly instead of seeding a state the application could never reach.
21
- - A failure is a console version of stopping in a debugger: the whole story, where it stopped and the spec line there, and the engine's state at that moment, written so an agent with no knowledge of this package can fix the test, and complete enough that a CI log alone is enough to reproduce it.
22
- - The kit author writes hooks and the package infers every type from them. There is one kit, built on Effect: a hook is a plain function, or a generator function when it needs a service or fails with a typed error, so no definition writes `Effect.gen` and a plain engine needs no Effect at all.
23
- - A spec writes only its story. The kit hands it an `it` that already knows the engine's services, so a spec never builds a test harness, and names every test after its genre, so a report shows which engine it ran and a run can pick one engine's stories.
24
-
25
- ## Trade-offs
26
-
27
- - **Real engine over speed.** A story is slower than a unit test with a mock. We accept that, and keep engines fast enough to step in a test instead of faking them.
28
- - **Hooks over freedom.** The kit fixes the shape of an engine: create, stages, after-stage hooks, a step, a state to inspect, verbs. An engine that does not fit should change its kit's hooks, not bypass the kit.
29
- - **Cheap locations over complete ones.** The failure shows one spec line, where the story stopped, found by matching the spec's path in the failure's stack or in the call site of the story's last line. A failure raised deep inside an Effect verb may have none; finding one would take machinery we do not want.
30
-
31
- ## What it will not do
32
-
33
- - Run without Vitest, or print a failure anywhere but the failing test's own error.
34
- - Own any application's traits, verbs or steps; they belong to the application's `test-support/`.
35
- - Mock the application's own code, or help a test reach into an engine's internals. A fake belongs only at a true external boundary, and the kit's `create` is where it is wired in.
package/docs/roadmap.md DELETED
@@ -1,26 +0,0 @@
1
- # Roadmap
2
-
3
- This file owns the story kit's implementation, next work and open questions. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns composed application features and host-level validation. Those roadmaps link to each other rather than repeating a completion checklist.
4
-
5
- ## Built
6
-
7
- - [x] `storyKit(definition)`: one Effect kit per engine, with every type and the services its layer provides inferred from its hooks. Plain engines plug in with plain functions.
8
- - [x] Traits seeded stage by stage, with a hook after each stage, and refusal of an impossible setup.
9
- - [x] The kit's own `it`, built on `@shivaedev/effect-test`, which hands the body the verbs and the story and names every test after the kit's genre. Vitest tags per genre are opt-in through `genreTags(...kitNames)` in the Vitest config.
10
- - [x] `story.runUntil(condition)`: steps the engine until the condition holds, stops on what `run.failed` reports, and stops after a step budget with a diagnosis, keeping every hook's typed failure in its error channel.
11
- - [x] A failed test prints a short guide, its story, where it stopped with the spec line there, and the engine's state, inline or in a file under `node_modules/.cache/test-story/`, broken at 120 columns.
12
- - [x] `domEnvironment` keeps Node fetch classes for an in-process host and happy-dom fetch for the client; `domFetch(baseUrl)` connects the window to that host, with a starter story proving Better Auth sign-up, sign-in, session reads and sign-out on PostgreSQL through cookies.
13
- - [x] `domNavigate` loads a routed document into happy-dom, updates its location and returns a response whose body remains readable. [Document transport stories](../src/server/documentTransport.dom.spec.ts) assert the rendered heading, location and response body.
14
- - [x] `outbound` routes exact hostnames to fake HTTP servers while preserving request paths, queries, methods, headers and bodies, and restores Node routing after successful and failed stories. [Outbound stories](../src/server/outbound.spec.ts) assert routing and restoration; [document transport stories](../src/server/documentTransport.dom.spec.ts) cover redirects, cookies and XMLHttpRequest; [DOM refusal tests](../src/server/domOutbound.spec.ts) assert that unrouted external fetches, document requests and redirects fail the story even when application code catches the error.
15
- - [x] In-process host creation separates request traces from the story span: requests without trace headers get distinct root traces, supplied trace headers continue their trace, request work stays under the request span, and traits and steps stay under the story span. [Host tracing stories](../src/server/hostTracing.spec.ts) assert span parents, trace IDs and completed request spans.
16
- - [x] Creation, traits and stage `after` hooks can return Effects directly, with services from the kit's Layer and resources released in the story's Scope, covered by [Effect hook stories](../src/server/effectHooks.spec.ts).
17
- - [x] Kit hooks and steps support a live clock and a per-story TestClock override. [Live-time stories](../src/server/liveTime.spec.ts) cover polling real workers with `withLive` without advancing virtual token expiry.
18
-
19
- ## Next
20
-
21
- - [ ] Move the Work Fleet engine's hand-written story DSL onto the kit and let what it needs decide the next hook.
22
-
23
- ## Open questions
24
-
25
- - A long story prints in full. Should a story of thousands of lines print its traits and its last lines, and write the rest to a file the way a large engine state is written?
26
- - `inspect` runs synchronously when the test has failed. An engine whose state lives behind an Effect service cannot be read then. Is that worth an Effect inspect, captured when the failure happens?