@shivaedev/test-story 0.2.1 → 0.4.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 +19 -0
- package/README.md +110 -1
- package/dist/server/domEnvironment.config.d.ts +4 -0
- package/dist/server/domEnvironment.config.d.ts.map +1 -0
- package/dist/server/domEnvironment.config.js +49 -0
- package/dist/server/domEnvironment.config.js.map +1 -0
- package/dist/server/domFetch.d.ts +2 -0
- package/dist/server/domFetch.d.ts.map +1 -0
- package/dist/server/domFetch.js +24 -0
- package/dist/server/domFetch.js.map +1 -0
- package/dist/server/domNavigate.d.ts +2 -0
- package/dist/server/domNavigate.d.ts.map +1 -0
- package/dist/server/domNavigate.js +10 -0
- package/dist/server/domNavigate.js.map +1 -0
- package/dist/server/internal/documentRedirect.d.ts +7 -0
- package/dist/server/internal/documentRedirect.d.ts.map +1 -0
- package/dist/server/internal/documentRedirect.js +26 -0
- package/dist/server/internal/documentRedirect.js.map +1 -0
- package/dist/server/internal/documentRequest.d.ts +6 -0
- package/dist/server/internal/documentRequest.d.ts.map +1 -0
- package/dist/server/internal/documentRequest.js +21 -0
- package/dist/server/internal/documentRequest.js.map +1 -0
- package/dist/server/internal/documentResponse.d.ts +4 -0
- package/dist/server/internal/documentResponse.d.ts.map +1 -0
- package/dist/server/internal/documentResponse.js +42 -0
- package/dist/server/internal/documentResponse.js.map +1 -0
- package/dist/server/internal/documentTransport.d.ts +3 -0
- package/dist/server/internal/documentTransport.d.ts.map +1 -0
- package/dist/server/internal/documentTransport.js +40 -0
- package/dist/server/internal/documentTransport.js.map +1 -0
- package/dist/server/internal/errors.d.ts +1 -0
- package/dist/server/internal/errors.d.ts.map +1 -1
- package/dist/server/internal/errors.js +3 -0
- package/dist/server/internal/errors.js.map +1 -1
- package/dist/server/internal/routeOutbound.d.ts +4 -0
- package/dist/server/internal/routeOutbound.d.ts.map +1 -0
- package/dist/server/internal/routeOutbound.js +58 -0
- package/dist/server/internal/routeOutbound.js.map +1 -0
- package/dist/server/internal/seed.d.ts +1 -0
- package/dist/server/internal/seed.d.ts.map +1 -1
- package/dist/server/internal/seed.js.map +1 -1
- package/dist/server/internal/storyIt.d.ts.map +1 -1
- package/dist/server/internal/storyIt.js +8 -1
- package/dist/server/internal/storyIt.js.map +1 -1
- package/dist/server/storyKit.d.ts +1 -0
- package/dist/server/storyKit.d.ts.map +1 -1
- package/dist/server/storyKit.js.map +1 -1
- package/docs/roadmap.md +1 -0
- package/package.json +9 -2
- package/src/server/domEnvironment.config.ts +51 -0
- package/src/server/domFetch.ts +26 -0
- package/src/server/domNavigate.ts +9 -0
- package/src/server/internal/documentRedirect.ts +28 -0
- package/src/server/internal/documentRequest.ts +26 -0
- package/src/server/internal/documentResponse.ts +51 -0
- package/src/server/internal/documentTransport.ts +41 -0
- package/src/server/internal/errors.ts +9 -0
- package/src/server/internal/routeOutbound.ts +72 -0
- package/src/server/internal/seed.ts +1 -0
- package/src/server/internal/storyIt.ts +10 -1
- package/src/server/storyKit.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 - 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `domNavigate(url?: string | URL): Promise<Response>` follows document redirects, keeps cookies and loads the final HTML for DOM stories, including social sign-in through an outbound OAuth fake.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Keep Node fetch for in-process hosts and route the DOM document's asynchronous HTTP requests through the same outbound fakes. Unrouted external hosts fail DOM stories with the hostname, even when application code catches the request error.
|
|
12
|
+
- Pass the `domFetch` result explicitly to browser clients (for Better Auth, `fetchOptions.customFetchImpl`); global fetch now belongs to the host. Happy-dom remains an optional peer for the DOM environment.
|
|
13
|
+
|
|
14
|
+
## 0.3.0 - 2026-10-17
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `@shivaedev/test-story/domEnvironment.config.ts` exports a Vitest environment with Node's global `Request`, `Response`, `Headers` and abort classes for an in-process HTTP host, while happy-dom fetch keeps the client's cookie jar. Fetch accepts native headers and request inputs. Better Auth sign-up, sign-in, session reads and sign-out are tested against the starter's real PostgreSQL host, including its auth client and client response inspection.
|
|
19
|
+
- `domFetch(baseUrl)` from `@shivaedev/test-story/domFetch.ts` sets the happy-dom page origin to its host and resolves relative fetch requests.
|
|
20
|
+
- `storyKit` accepts `outbound: Readonly<Record<string, string>>` to route selected hostnames from Node's built-in `fetch` to fake server origins for each story, then restore the previous dispatcher after success or failure. Other hosts pass through unchanged.
|
|
21
|
+
|
|
3
22
|
## 0.2.1 - 2026-10-16
|
|
4
23
|
|
|
5
24
|
### Fixed
|
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ This is the bakery kit this package tests itself with. `emptyBakery`, `stillWait
|
|
|
107
107
|
| Hook | Says |
|
|
108
108
|
| --- | --- |
|
|
109
109
|
| `name` | What the engine is called in failures, "the bakery", and the kit's genre, "Bakery Story". |
|
|
110
|
+
| `outbound` | Optional. A map of exact hostnames to fake server origins for Node fetch and the DOM environment's document requests. |
|
|
110
111
|
| `around(effect)` | Optional. Wraps creation, staged seeding and the body; worker Layer acquisition remains outside it. It may require the worker services and per-test Scope. |
|
|
111
112
|
| `clock` | Optional. `"test"` by default, or `"live"` for HTTP and DOM stories. A story’s `options.clock` takes precedence. |
|
|
112
113
|
| `create()` | A fresh engine. Every story starts from a new one. |
|
|
@@ -130,6 +131,21 @@ export default defineConfig({
|
|
|
130
131
|
|
|
131
132
|
`genreTags(...kitNames)` declares one tag per kit, named after it: `bakery-story` and `mill-story` here, and `game-store-story` for a kit named "game store". A kit tags its tests only with a genre the config declares, because Vitest refuses a test whose tag the config leaves out, so a config without the line runs every story untagged and prints nothing about it. With the tags declared, `vitest run --tags-filter=mill-story` runs the mill's stories and skips the bakery's, and `--tags-filter` takes any of Vitest's tag expressions, such as `'!mill-story'`. A root config whose inline projects extend the package's config gets these tags from it, so `--tags-filter` works from the root too.
|
|
132
133
|
|
|
134
|
+
#### Routing outbound HTTP
|
|
135
|
+
|
|
136
|
+
When application code calls a hard-coded third-party URL, set `outbound` on its kit to send those requests to a fake server:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const app = storyKit({
|
|
140
|
+
...appDefinition,
|
|
141
|
+
outbound: { "github.com": fakeUrl, "api.github.com": fakeUrl },
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`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.
|
|
146
|
+
|
|
147
|
+
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`.
|
|
148
|
+
|
|
133
149
|
### 2. Once per feature: traits and verbs
|
|
134
150
|
|
|
135
151
|
A trait is a line and a change, tagged with a stage:
|
|
@@ -270,7 +286,100 @@ Application-authored seed builders reuse application schemas, validate overrides
|
|
|
270
286
|
|
|
271
287
|
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.
|
|
272
288
|
|
|
273
|
-
The example proves DOM and loopback HTTP behavior, not browser
|
|
289
|
+
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`.
|
|
290
|
+
|
|
291
|
+
### DOM stories with cookie sessions
|
|
292
|
+
|
|
293
|
+
Install `happy-dom@20.14.6` as a dev dependency. Select the `@shivaedev/test-story/domEnvironment.config.ts` Vitest environment for DOM tests that start their host in the same process:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
import { fileURLToPath } from "node:url";
|
|
297
|
+
import { defineConfig } from "vitest/config";
|
|
298
|
+
|
|
299
|
+
export default defineConfig({
|
|
300
|
+
test: {
|
|
301
|
+
environment: fileURLToPath(import.meta.resolve("@shivaedev/test-story/domEnvironment.config.ts")),
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The environment keeps Node's global `fetch`, `Request`, `Response`, `Headers`, `AbortController` and `AbortSignal` for the in-process host. The separate `window` keeps happy-dom's constructors and cookie jar. Its asynchronous HTTP requests use Node fetch through the same `outbound` dispatcher, including XMLHttpRequest. Synchronous document HTTP requests are refused. The starter's [Vitest config](https://github.com/ShivaeDev/platform/blob/main/examples/starter/vitest.config.ts) selects this environment only for its DOM project while retaining the shared test projects. Use Vitest's standard forks or threads pool.
|
|
307
|
+
|
|
308
|
+
`domFetch(baseUrl)` from `@shivaedev/test-story/domFetch.ts` sets the document's location to the real HTTP host and returns a document fetch that resolves relative request strings against that URL. It translates Node `Request` and `Headers` inputs for happy-dom. Responses have happy-dom's status, headers and body methods; they are not instances of Node's global `Response`. The document keeps and sends cookies, including HttpOnly session cookies. Keep the default `same-origin` credentials policy for same-origin requests, and pass this fetch explicitly to browser clients, such as Better Auth's `fetchOptions.customFetchImpl`. The host continues to use global Node fetch without browser cookies.
|
|
309
|
+
|
|
310
|
+
Call it in the kit's `create` hook after starting the host:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import { createAuthClient } from "better-auth/react";
|
|
314
|
+
import { domFetch } from "@shivaedev/test-story/domFetch.ts";
|
|
315
|
+
|
|
316
|
+
const cookieApp = storyKit({
|
|
317
|
+
around: withIsolatedDatabase,
|
|
318
|
+
clock: "live",
|
|
319
|
+
*create() {
|
|
320
|
+
const { baseUrl } = yield* startHost();
|
|
321
|
+
const fetch = domFetch(baseUrl);
|
|
322
|
+
const client = createAuthClient({ baseURL: baseUrl, fetchOptions: { customFetchImpl: fetch } });
|
|
323
|
+
return { client, fetch };
|
|
324
|
+
},
|
|
325
|
+
layer: postgres,
|
|
326
|
+
name: "cookie auth",
|
|
327
|
+
stages: ["account"],
|
|
328
|
+
verbs: ({ client, fetch }, story) => authVerbs(client, fetch, story.tell),
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`startHost`, `postgres` and `authVerbs` are application plumbing. The starter's [cookie application kit](https://github.com/ShivaeDev/platform/blob/main/examples/starter/src/test-support/cookieApp.ts) shows the full setup. Its Better Auth client signs up and signs in, and its verbs read `/api/auth/get-session` and post to `/api/auth/sign-out` through the DOM fetch. They do not copy cookies into request headers or use bearer tokens.
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
cookieApp.it("sign-up and sign-in keep their cookie sessions against the PostgreSQL host", [], function* ({ signUp, signIn, signOut, sessionIs }) {
|
|
336
|
+
yield* signUp();
|
|
337
|
+
yield* sessionIs("alice@starter.test");
|
|
338
|
+
yield* signOut();
|
|
339
|
+
yield* sessionIs(null);
|
|
340
|
+
yield* signIn();
|
|
341
|
+
yield* sessionIs("alice@starter.test");
|
|
342
|
+
yield* signOut();
|
|
343
|
+
yield* sessionIs(null);
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
This story proves Better Auth cookie sessions across real loopback HTTP requests from happy-dom with an in-process PostgreSQL host. It also reads sessions through Better Auth's client and checks the fetch response's status, headers and JSON body.
|
|
348
|
+
|
|
349
|
+
#### Social sign-in through an outbound provider
|
|
350
|
+
|
|
351
|
+
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.
|
|
352
|
+
|
|
353
|
+
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.
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
import { domNavigate } from "@shivaedev/test-story/domNavigate.ts";
|
|
357
|
+
|
|
358
|
+
const socialApp = storyKit({
|
|
359
|
+
...socialDefinition,
|
|
360
|
+
outbound: { "github.com": fakeUrl, "api.github.com": fakeUrl },
|
|
361
|
+
verbs: (engine, story) => ({
|
|
362
|
+
signInWithGitHub: () => Effect.promise(async () => {
|
|
363
|
+
story.tell("Alice clicks Sign in with GitHub");
|
|
364
|
+
await engine.screen.click();
|
|
365
|
+
await domNavigate();
|
|
366
|
+
}),
|
|
367
|
+
signedInPageIs: (email: string) => Effect.promise(async () => {
|
|
368
|
+
expect(document.querySelector("h1")?.textContent).toBe(`Signed in as ${email}`);
|
|
369
|
+
expect(await (await engine.fetch("/api/auth/get-session")).json()).toMatchObject({ user: { email } });
|
|
370
|
+
}),
|
|
371
|
+
}),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
socialApp.it("a social sign-in returns with a cookie session", [], function* ({ signInWithGitHub, signedInPageIs }) {
|
|
375
|
+
yield* signInWithGitHub();
|
|
376
|
+
yield* signedInPageIs("alice@social.test");
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`socialDefinition` starts the host and renders the screen with a Better Auth client whose `customFetchImpl` is `domFetch(baseUrl)`. The [story](https://github.com/ShivaeDev/platform/blob/main/examples/starter/src/server/socialSessions.dom.spec.ts) also checks the authorize, token and user requests at the fake, persisted PostgreSQL rows, and an HttpOnly session cookie read through a later document request. It does not copy cookies into request headers.
|
|
381
|
+
|
|
382
|
+
`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.
|
|
274
383
|
|
|
275
384
|
### Install and limits
|
|
276
385
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domEnvironment.config.d.ts","sourceRoot":"","sources":["../../src/server/domEnvironment.config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,gBAAgB,CAAC;AAElE,QAAA,MAAM,cAAc,EAAE,WA4CrB,CAAC;eAEa,cAAc"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { GlobalWindow } from "happy-dom";
|
|
3
|
+
import { populateGlobal } from "vitest/runtime";
|
|
4
|
+
const domEnvironment = {
|
|
5
|
+
name: "test-story-dom",
|
|
6
|
+
async setup(global, { happyDOM = {} }) {
|
|
7
|
+
// Vitest loads environments outside the project's source resolution conditions.
|
|
8
|
+
const load = createRequire(import.meta.url);
|
|
9
|
+
const { documentTransport } = load("#internal/documentTransport.ts");
|
|
10
|
+
const { installOutbound } = load("#internal/routeOutbound.ts");
|
|
11
|
+
const native = Object.fromEntries(["fetch", "AbortController", "AbortSignal", "Headers", "Request", "Response"].map((name) => [name, Reflect.get(global, name)]));
|
|
12
|
+
const restoreOutbound = await installOutbound({}, true);
|
|
13
|
+
const win = new GlobalWindow({
|
|
14
|
+
...happyDOM,
|
|
15
|
+
console: global.console,
|
|
16
|
+
settings: { ...happyDOM.settings, disableErrorCapturing: true },
|
|
17
|
+
url: happyDOM.url ?? "http://localhost:3000",
|
|
18
|
+
});
|
|
19
|
+
win.happyDOM.settings.fetch.interceptor = documentTransport(global.fetch);
|
|
20
|
+
const { keys, originals } = populateGlobal(global, win, { bindFunctions: true });
|
|
21
|
+
for (const [name, value] of Object.entries(native)) {
|
|
22
|
+
Object.defineProperty(global, name, { configurable: true, value, writable: true });
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(global, "window", { configurable: true, value: win, writable: true });
|
|
25
|
+
Object.defineProperty(win.document, "defaultView", { configurable: true, get: () => win });
|
|
26
|
+
Object.defineProperty(global, Symbol.for("test-story.dom"), { configurable: true, value: true });
|
|
27
|
+
return {
|
|
28
|
+
async teardown(target) {
|
|
29
|
+
try {
|
|
30
|
+
await win.happyDOM.abort();
|
|
31
|
+
win.close();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
for (const key of keys) {
|
|
35
|
+
Reflect.deleteProperty(target, key);
|
|
36
|
+
}
|
|
37
|
+
for (const [key, descriptor] of originals) {
|
|
38
|
+
Object.defineProperty(target, key, descriptor);
|
|
39
|
+
}
|
|
40
|
+
Reflect.deleteProperty(target, Symbol.for("test-story.dom"));
|
|
41
|
+
restoreOutbound();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
viteEnvironment: "client",
|
|
47
|
+
};
|
|
48
|
+
export default domEnvironment;
|
|
49
|
+
//# sourceMappingURL=domEnvironment.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domEnvironment.config.js","sourceRoot":"","sources":["../../src/server/domEnvironment.config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domFetch.d.ts","sourceRoot":"","sources":["../../src/server/domFetch.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,UAAU,CAAC,KAAK,CAyBjE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function domFetch(baseUrl) {
|
|
2
|
+
const base = new URL(baseUrl);
|
|
3
|
+
window.location.href = base.href;
|
|
4
|
+
const transport = window.fetch.bind(window);
|
|
5
|
+
return (input, init) => {
|
|
6
|
+
if (input instanceof Request) {
|
|
7
|
+
const request = new Request(input, init);
|
|
8
|
+
return transport(request.url, {
|
|
9
|
+
body: request.body,
|
|
10
|
+
cache: request.cache,
|
|
11
|
+
credentials: request.credentials,
|
|
12
|
+
headers: [...request.headers],
|
|
13
|
+
method: request.method,
|
|
14
|
+
mode: request.mode,
|
|
15
|
+
redirect: request.redirect,
|
|
16
|
+
referrer: request.referrer,
|
|
17
|
+
referrerPolicy: request.referrerPolicy,
|
|
18
|
+
signal: request.signal,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return transport(typeof input === "string" ? new URL(input, base).href : input, init?.headers instanceof Headers ? { ...init, headers: [...init.headers] } : init);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=domFetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domFetch.js","sourceRoot":"","sources":["../../src/server/domFetch.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ,CAAC,OAAe;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,IAAI,KAAK,YAAY,OAAO,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACzC,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;gBAC7B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;aACtB,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CACf,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAC7D,IAAI,EAAE,OAAO,YAAY,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACjF,CAAC;IACH,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domNavigate.d.ts","sourceRoot":"","sources":["../../src/server/domNavigate.ts"],"names":[],"mappings":"AAAA,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAM,GAAG,GAA0B,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ7F"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export async function domNavigate(url = window.location.href) {
|
|
2
|
+
const response = await window.fetch(new URL(url, window.location.href), { credentials: "include" });
|
|
3
|
+
const html = await response.clone().text();
|
|
4
|
+
window.location.href = response.url;
|
|
5
|
+
document.open();
|
|
6
|
+
document.write(html);
|
|
7
|
+
document.close();
|
|
8
|
+
return response;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=domNavigate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domNavigate.js","sourceRoot":"","sources":["../../src/server/domNavigate.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAG,GAAiB,MAAM,CAAC,QAAQ,CAAC,IAAI;IACzE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;IACpG,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChB,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Request } from "happy-dom";
|
|
2
|
+
import type BrowserWindow from "happy-dom/lib/window/BrowserWindow.js";
|
|
3
|
+
export declare function documentRedirect(request: Request, window: BrowserWindow, target: URL, status: number, body: Buffer<ArrayBuffer> | null): {
|
|
4
|
+
body: Buffer<ArrayBuffer> | null;
|
|
5
|
+
request: Request;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=documentRedirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentRedirect.d.ts","sourceRoot":"","sources":["../../../src/server/internal/documentRedirect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,aAAa,MAAM,uCAAuC,CAAC;AAEvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,IAAI;IAcrI,IAAI;IACJ,OAAO;EASR"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function documentRedirect(request, window, target, status, body) {
|
|
2
|
+
const headers = new window.Headers(request.headers);
|
|
3
|
+
if (target.origin !== new URL(request.url).origin) {
|
|
4
|
+
for (const name of ["authorization", "cookie", "proxy-authorization"]) {
|
|
5
|
+
headers.delete(name);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const get = (status === 303 && request.method !== "HEAD") || ([301, 302].includes(status) && request.method === "POST");
|
|
9
|
+
if (get) {
|
|
10
|
+
headers.delete("content-type");
|
|
11
|
+
headers.delete("content-length");
|
|
12
|
+
}
|
|
13
|
+
const nextBody = get ? null : body;
|
|
14
|
+
return {
|
|
15
|
+
body: nextBody,
|
|
16
|
+
request: new window.Request(target.href, {
|
|
17
|
+
body: nextBody,
|
|
18
|
+
credentials: request.credentials,
|
|
19
|
+
headers,
|
|
20
|
+
method: get ? "GET" : request.method,
|
|
21
|
+
redirect: request.redirect,
|
|
22
|
+
signal: request.signal,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=documentRedirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentRedirect.js","sourceRoot":"","sources":["../../../src/server/internal/documentRedirect.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAAE,MAAqB,EAAE,MAAW,EAAE,MAAc,EAAE,IAAgC;IACtI,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC;YACvE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACF,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACxH,IAAI,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;YACxC,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,OAAO;YACP,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM;YACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;SACtB,CAAC;KACF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Request } from "happy-dom";
|
|
2
|
+
export declare function documentRequest(fetch: typeof globalThis.fetch, request: Request, body: Buffer<ArrayBuffer> | null, headers: Readonly<Record<string, string>>): Promise<{
|
|
3
|
+
release: () => void;
|
|
4
|
+
response: Response;
|
|
5
|
+
}>;
|
|
6
|
+
//# sourceMappingURL=documentRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentRequest.d.ts","sourceRoot":"","sources":["../../../src/server/internal/documentRequest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,wBAAsB,eAAe,CACpC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,EAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,IAAI,EAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;GAmBzC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export async function documentRequest(fetch, request, body, headers) {
|
|
2
|
+
const controller = new AbortController();
|
|
3
|
+
function abort() {
|
|
4
|
+
controller.abort(request.signal.reason);
|
|
5
|
+
}
|
|
6
|
+
function release() {
|
|
7
|
+
request.signal.removeEventListener("abort", abort);
|
|
8
|
+
}
|
|
9
|
+
if (request.signal.aborted) {
|
|
10
|
+
abort();
|
|
11
|
+
}
|
|
12
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
13
|
+
try {
|
|
14
|
+
return { release, response: await fetch(request.url, { body, headers, method: request.method, redirect: "manual", signal: controller.signal }) };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
release();
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=documentRequest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentRequest.js","sourceRoot":"","sources":["../../../src/server/internal/documentRequest.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,KAA8B,EAC9B,OAAgB,EAChB,IAAgC,EAChC,OAAyC;IAEzC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,SAAS,KAAK;QACb,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IACD,SAAS,OAAO;QACf,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC5B,KAAK,EAAE,CAAC;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC;QACJ,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IAClJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;QACV,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Headers, IBrowserFrame, Request } from "happy-dom";
|
|
2
|
+
export declare function documentResponseHeaders(response: Response, request: Request, frame: IBrowserFrame): Headers;
|
|
3
|
+
export declare function documentResponse(response: Response, request: Request, frame: IBrowserFrame, release: () => void, redirected: boolean, headers: Headers): import("happy-dom").Response;
|
|
4
|
+
//# sourceMappingURL=documentResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentResponse.d.ts","sourceRoot":"","sources":["../../../src/server/internal/documentResponse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGjE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,WAQjG;AAED,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,MAAM,IAAI,EACnB,UAAU,EAAE,OAAO,EACnB,OAAO,EAAE,OAAO,gCAahB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import FetchResponseHeaderUtility from "happy-dom/lib/fetch/utilities/FetchResponseHeaderUtility.js";
|
|
3
|
+
export function documentResponseHeaders(response, request, frame) {
|
|
4
|
+
return FetchResponseHeaderUtility.parseResponseHeaders({
|
|
5
|
+
browserFrame: frame,
|
|
6
|
+
rawHeaders: [...response.headers]
|
|
7
|
+
.flatMap(([name, value]) => (name === "set-cookie" ? [] : [name, value]))
|
|
8
|
+
.concat(response.headers.getSetCookie().flatMap((cookie) => ["set-cookie", cookie])),
|
|
9
|
+
"requestURL": new URL(request.url),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function documentResponse(response, request, frame, release, redirected, headers) {
|
|
13
|
+
if (response.body === null) {
|
|
14
|
+
release();
|
|
15
|
+
}
|
|
16
|
+
const result = new frame.window.Response(response.body === null ? null : Readable.toWeb(Readable.from(chunks(response.body, release))), {
|
|
17
|
+
headers,
|
|
18
|
+
status: response.status,
|
|
19
|
+
statusText: response.statusText,
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(result, "url", { value: request.url });
|
|
22
|
+
Object.defineProperty(result, "redirected", { value: redirected });
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
async function* chunks(stream, release) {
|
|
26
|
+
const reader = stream.getReader();
|
|
27
|
+
try {
|
|
28
|
+
while (true) {
|
|
29
|
+
const { done, value } = await reader.read();
|
|
30
|
+
if (done) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
yield value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
release();
|
|
38
|
+
await reader.cancel();
|
|
39
|
+
reader.releaseLock();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=documentResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentResponse.js","sourceRoot":"","sources":["../../../src/server/internal/documentResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,0BAA0B,MAAM,6DAA6D,CAAC;AAErG,MAAM,UAAU,uBAAuB,CAAC,QAAkB,EAAE,OAAgB,EAAE,KAAoB;IACjG,OAAO,0BAA0B,CAAC,oBAAoB,CAAC;QACtD,YAAY,EAAE,KAAK;QACnB,UAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC;aAC/B,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;aACxE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;QACrF,YAAY,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;KAClC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC/B,QAAkB,EAClB,OAAgB,EAChB,KAAoB,EACpB,OAAmB,EACnB,UAAmB,EACnB,OAAgB;IAEhB,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE;QACvI,OAAO;QACP,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;KAC/B,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC;AACf,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC,MAAkC,EAAE,OAAmB;IAC7E,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,IAAI,CAAC;QACJ,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;YAAS,CAAC;QACV,OAAO,EAAE,CAAC;QACV,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,CAAC,WAAW,EAAE,CAAC;IACtB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentTransport.d.ts","sourceRoot":"","sources":["../../../src/server/internal/documentTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAOnD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,GAAG,iBAAiB,CAiCnF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import FetchRequestHeaderUtility from "happy-dom/lib/fetch/utilities/FetchRequestHeaderUtility.js";
|
|
2
|
+
import WindowBrowserContext from "happy-dom/lib/window/WindowBrowserContext.js";
|
|
3
|
+
import { documentRedirect } from "#internal/documentRedirect.ts";
|
|
4
|
+
import { documentRequest } from "#internal/documentRequest.ts";
|
|
5
|
+
import { documentResponse, documentResponseHeaders } from "#internal/documentResponse.ts";
|
|
6
|
+
export function documentTransport(fetch) {
|
|
7
|
+
return {
|
|
8
|
+
async beforeAsyncRequest({ request, window }) {
|
|
9
|
+
const frame = new WindowBrowserContext(window).getBrowserFrame();
|
|
10
|
+
if (!frame) {
|
|
11
|
+
throw new Error("test-story cannot fetch after the document has closed");
|
|
12
|
+
}
|
|
13
|
+
let current = request;
|
|
14
|
+
let body = current.body === null ? null : Buffer.from(await current.arrayBuffer());
|
|
15
|
+
for (let redirects = 0; redirects <= 20; redirects += 1) {
|
|
16
|
+
const headers = FetchRequestHeaderUtility.getRequestHeaders({ browserFrame: frame, request: current, window });
|
|
17
|
+
delete headers.Connection;
|
|
18
|
+
const { release, response } = await documentRequest(fetch, current, body, headers);
|
|
19
|
+
const responseHeaders = documentResponseHeaders(response, current, frame);
|
|
20
|
+
const location = response.headers.get("location");
|
|
21
|
+
if (![301, 302, 303, 307, 308].includes(response.status) || location === null || current.redirect === "manual") {
|
|
22
|
+
return documentResponse(response, current, frame, release, redirects > 0, responseHeaders);
|
|
23
|
+
}
|
|
24
|
+
await response.body?.cancel();
|
|
25
|
+
release();
|
|
26
|
+
if (current.redirect === "error" || redirects === 20) {
|
|
27
|
+
throw new Error(`test-story cannot follow redirect from ${current.url}`);
|
|
28
|
+
}
|
|
29
|
+
const next = documentRedirect(current, window, new URL(location, current.url), response.status, body);
|
|
30
|
+
current = next.request;
|
|
31
|
+
body = next.body;
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`test-story reached the redirect limit for ${request.url}`);
|
|
34
|
+
},
|
|
35
|
+
beforeSyncRequest({ request }) {
|
|
36
|
+
throw new Error(`test-story refuses synchronous document requests to ${new URL(request.url).hostname}; use fetch instead`);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=documentTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentTransport.js","sourceRoot":"","sources":["../../../src/server/internal/documentTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function unrouted(hostname: string): Error;
|
|
1
2
|
export declare function refusal(name: string, line: string, cause: unknown): Error;
|
|
2
3
|
export declare function broken(name: string, steps: number, reason: string): Error;
|
|
3
4
|
export declare function unfinished(name: string, steps: number, diagnosis: string | undefined): Error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/server/internal/errors.ts"],"names":[],"mappings":"AAGA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,CAQzE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAOzE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAO5F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAO7C"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/server/internal/errors.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAOhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,CAQzE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAOzE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAO5F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAO7C"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describeValue } from "#internal/render.ts";
|
|
2
2
|
import { explained } from "#internal/wrap.ts";
|
|
3
|
+
export function unrouted(hostname) {
|
|
4
|
+
return new Error(explained(`test-story refused an outbound request to ${hostname}`, "Add this hostname to the story kit's outbound map and point it at a fake server. Only loopback requests may run without a route."));
|
|
5
|
+
}
|
|
3
6
|
export function refusal(name, line, cause) {
|
|
4
7
|
return new Error(explained(`the trait "${line}" refused to set up the ${name}: ${describeValue(cause)}`, `a trait throws when the ${name} it asks for cannot exist. Give the test traits that fit together, or fix the trait in the ${name} story kit if this ${name} should be possible.`), { cause });
|
|
5
8
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/server/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,KAAc;IACjE,OAAO,IAAI,KAAK,CACf,SAAS,CACR,cAAc,IAAI,2BAA2B,IAAI,KAAK,aAAa,CAAC,KAAK,CAAC,EAAE,EAC5E,2BAA2B,IAAI,8FAA8F,IAAI,sBAAsB,IAAI,sBAAsB,CACjL,EACD,EAAE,KAAK,EAAE,CACT,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAa,EAAE,MAAc;IACjE,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,gBAAgB,KAAK,WAAW,MAAM,EAAE,EACnD,qBAAqB,IAAI,kCAAkC,IAAI,oGAAoG,CACnK,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa,EAAE,SAA6B;IACpF,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,QAAQ,KAAK,mDAAmD,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,EAAE,EAC5H,cAAc,IAAI,sHAAsH,CACxI,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACrC,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,4DAA4D,IAAI,EAAE,EAC7E,YAAY,IAAI,+DAA+D,IAAI,eAAe,CAClG,CACD,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/server/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACxC,OAAO,IAAI,KAAK,CACf,SAAS,CACR,6CAA6C,QAAQ,EAAE,EACvD,kIAAkI,CAClI,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,KAAc;IACjE,OAAO,IAAI,KAAK,CACf,SAAS,CACR,cAAc,IAAI,2BAA2B,IAAI,KAAK,aAAa,CAAC,KAAK,CAAC,EAAE,EAC5E,2BAA2B,IAAI,8FAA8F,IAAI,sBAAsB,IAAI,sBAAsB,CACjL,EACD,EAAE,KAAK,EAAE,CACT,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAa,EAAE,MAAc;IACjE,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,gBAAgB,KAAK,WAAW,MAAM,EAAE,EACnD,qBAAqB,IAAI,kCAAkC,IAAI,oGAAoG,CACnK,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa,EAAE,SAA6B;IACpF,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,QAAQ,KAAK,mDAAmD,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,EAAE,EAC5H,cAAc,IAAI,sHAAsH,CACxI,CACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACrC,OAAO,IAAI,KAAK,CACf,SAAS,CACR,OAAO,IAAI,4DAA4D,IAAI,EAAE,EAC7E,YAAY,IAAI,+DAA+D,IAAI,eAAe,CAClG,CACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export declare function installOutbound(routes: Readonly<Record<string, string>>, strict?: boolean): Promise<() => void>;
|
|
3
|
+
export declare function routeOutbound(routes: Readonly<Record<string, string>>): Effect.Effect<void, never, import("effect/Scope").Scope>;
|
|
4
|
+
//# sourceMappingURL=routeOutbound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { unrouted } from "#internal/errors.ts";
|
|
4
|
+
function isDispatcher(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && "dispatch" in value && typeof value.dispatch === "function";
|
|
6
|
+
}
|
|
7
|
+
export async function installOutbound(routes, strict = false) {
|
|
8
|
+
const origins = new Map(Object.entries(routes).map(([hostname, target]) => [hostname, new URL(target).origin]));
|
|
9
|
+
let refused;
|
|
10
|
+
// A data URL initializes Node's bundled Undici without sending an HTTP request.
|
|
11
|
+
await fetch("data:,");
|
|
12
|
+
const slots = ["undici.globalDispatcher.1", "undici.globalDispatcher.2"].flatMap((name) => {
|
|
13
|
+
const key = Symbol.for(name);
|
|
14
|
+
const previous = Reflect.get(globalThis, key);
|
|
15
|
+
return isDispatcher(previous) ? [{ key, previous }] : [];
|
|
16
|
+
});
|
|
17
|
+
if (slots.length === 0) {
|
|
18
|
+
throw new Error("test-story outbound routing needs Node's built-in fetch dispatcher. Run the stories in Node.js 24 or newer.");
|
|
19
|
+
}
|
|
20
|
+
function restore() {
|
|
21
|
+
for (const { key, previous } of slots) {
|
|
22
|
+
Object.defineProperty(globalThis, key, { value: previous });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
for (const { key, previous } of slots) {
|
|
27
|
+
const router = {
|
|
28
|
+
dispatch(options, handler) {
|
|
29
|
+
const hostname = new URL(options.origin).hostname;
|
|
30
|
+
const target = origins.get(hostname);
|
|
31
|
+
if (strict
|
|
32
|
+
&& target === undefined
|
|
33
|
+
&& !["localhost", "[::1]"].includes(hostname)
|
|
34
|
+
&& !(isIP(hostname) === 4 && hostname.startsWith("127."))) {
|
|
35
|
+
refused = unrouted(hostname);
|
|
36
|
+
throw refused;
|
|
37
|
+
}
|
|
38
|
+
return previous.dispatch(target === undefined ? options : { ...options, origin: target }, handler);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(globalThis, key, { value: router });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
restore();
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
return () => {
|
|
49
|
+
restore();
|
|
50
|
+
if (refused !== undefined) {
|
|
51
|
+
throw refused;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routeOutbound.js","sourceRoot":"","sources":["../../../src/server/internal/routeOutbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,7 @@ export interface LooseDefinition {
|
|
|
21
21
|
readonly inspect?: (engine: unknown) => unknown;
|
|
22
22
|
readonly layer?: AnyTestLayer;
|
|
23
23
|
readonly name: string;
|
|
24
|
+
readonly outbound?: Readonly<Record<string, string>>;
|
|
24
25
|
readonly run?: LooseRunHooks<unknown>;
|
|
25
26
|
readonly stages: readonly string[];
|
|
26
27
|
readonly verbs: (engine: unknown, story: LooseStory) => object;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/server/internal/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACnD,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,GAAG,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,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,EAAS,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACnD,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,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":"seed.js","sourceRoot":"","sources":["../../../src/server/internal/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAGnD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAA8C,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAkB,SAAS,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/server/internal/seed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAGnD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAA8C,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAkB,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA4BnE,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,UAA2B,EAAE,MAAe,EAAE,SAAoB,EAAE,KAA4B;IAChJ,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBAC1B,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CACnD,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3B,IAAI,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;wBACpC,OAAO,MAAM,CAAC,SAAS,CAAC;oBACzB,CAAC;oBACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACnC,SAAS,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;oBAC7C,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBAChE,CAAC,CAAC,CACF,CAAC;YACH,CAAC;QACF,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpC,CAAC;IACF,CAAC;AACF,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;
|
|
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;AAiCD,wBAAgB,OAAO,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAuB5D"}
|
|
@@ -2,6 +2,7 @@ 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";
|
|
5
6
|
import { start } from "#internal/start.ts";
|
|
6
7
|
function withTags(options, tags) {
|
|
7
8
|
if (typeof options === "number") {
|
|
@@ -34,8 +35,14 @@ function declaring(definition, genre, declare) {
|
|
|
34
35
|
}
|
|
35
36
|
export function storyIt(definition) {
|
|
36
37
|
const genre = genreOf(definition.name);
|
|
38
|
+
const outbound = definition.outbound ?? (Reflect.get(globalThis, Symbol.for("test-story.dom")) ? {} : undefined);
|
|
37
39
|
const { effectApp } = makeEffectIt({
|
|
38
|
-
around:
|
|
40
|
+
around: outbound === undefined
|
|
41
|
+
? definition.around
|
|
42
|
+
: (effect) => Effect.gen(function* () {
|
|
43
|
+
yield* routeOutbound(outbound);
|
|
44
|
+
return yield* definition.around?.(effect) ?? effect;
|
|
45
|
+
}),
|
|
39
46
|
clock: definition.clock,
|
|
40
47
|
layer: definition.layer ?? Layer.empty,
|
|
41
48
|
makeHarness: () => Effect.void,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storyIt.js","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAc,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAkB,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"storyIt.js","sourceRoot":"","sources":["../../../src/server/internal/storyIt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAc,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAkB,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAc3C,SAAS,QAAQ,CAAC,OAA+C,EAAE,IAAuB;IACzF,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9C,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;IAChC,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,SAAS,OAAO,CAAC,UAA2B,EAAE,KAA4B,EAAE,IAA2B;IACtG,OAAO,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAiB,EAAE,OAAoB;QAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC;AACH,CAAC;AAED,wHAAwH;AACxH,SAAS,SAAS,CAAC,UAA2B,EAAE,KAAY,EAAE,OAAqC;IAClG,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC;YACJ,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;gBAChF,MAAM,KAAK,CAAC;YACb,CAAC;YACD,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9C,CAAC;IACF,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,UAA2B;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjH,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC;QAClC,MAAM,EACL,QAAQ,KAAK,SAAS;YACrB,CAAC,CAAC,UAAU,CAAC,MAAM;YACnB,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CACX,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACnB,KAAK,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAC/B,OAAO,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;YACrD,CAAC,CAAC;QACN,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK;QACtC,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;KAC9B,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE;QAC7D,KAAK,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;QACpD,IAAI,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC;QAClD,KAAK,EAAE,CAAC,SAAkB,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvF,IAAI,EAAE,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC;QAClD,MAAM,EAAE,CAAC,SAAkB,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;KACzF,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -34,6 +34,7 @@ export interface StoryKitDefinition<TEngine, TStage extends string, TVerbs, TPro
|
|
|
34
34
|
readonly inspect?: (engine: TEngine) => unknown;
|
|
35
35
|
readonly layer?: Layer.Layer<TProvided, unknown, never>;
|
|
36
36
|
readonly name: string;
|
|
37
|
+
readonly outbound?: Readonly<Record<string, string>>;
|
|
37
38
|
readonly run?: RunHooks<TEngine, TStep, TFailed, TDiagnose>;
|
|
38
39
|
readonly stages: readonly [TStage, ...TStage[]];
|
|
39
40
|
readonly verbs: (engine: TEngine, story: Story<TEngine, TStep | TFailed | TDiagnose>) => TVerbs;
|
|
@@ -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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storyKit.js","sourceRoot":"","sources":["../../src/server/storyKit.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4E,MAAM,EAAmB,MAAM,qBAAqB,CAAC;AAGxI,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAc,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"storyKit.js","sourceRoot":"","sources":["../../src/server/storyKit.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4E,MAAM,EAAmB,MAAM,qBAAqB,CAAC;AAGxI,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAc,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuHhE,MAAM,UAAU,QAAQ,CAAC,UAA2B;IACnD,OAAO;QACN,EAAE,EAAE,OAAO,CAAC,UAAU,CAAC;QACvB,KAAK,EAAE,CAAC,KAAa,EAAE,IAAY,EAAE,KAAqD,EAAE,EAAE,CAC7F,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,MAAe,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACrE,MAAM;KACN,CAAC;AACH,CAAC"}
|
package/docs/roadmap.md
CHANGED
|
@@ -12,6 +12,7 @@ to each other rather than repeating a completion checklist.
|
|
|
12
12
|
- [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.
|
|
13
13
|
- [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.
|
|
14
14
|
- [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.
|
|
15
|
+
- [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.
|
|
15
16
|
|
|
16
17
|
## Next
|
|
17
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/test-story",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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": {
|
|
@@ -47,18 +47,25 @@
|
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@effect/vitest": "4.0.4",
|
|
50
|
-
"@shivaedev/quality": "0.
|
|
50
|
+
"@shivaedev/quality": "0.22.0",
|
|
51
51
|
"@types/node": "24.19.1",
|
|
52
52
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
53
53
|
"effect": "4.0.4",
|
|
54
|
+
"happy-dom": "20.14.6",
|
|
54
55
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
55
56
|
"vitest": "5.0.3"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@effect/vitest": "4.0.4",
|
|
59
60
|
"effect": "4.0.4",
|
|
61
|
+
"happy-dom": "20.14.6",
|
|
60
62
|
"vitest": "5.0.3"
|
|
61
63
|
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"happy-dom": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
62
69
|
"engines": {
|
|
63
70
|
"node": ">=24"
|
|
64
71
|
},
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { GlobalWindow } from "happy-dom";
|
|
3
|
+
import { type Environment, populateGlobal } from "vitest/runtime";
|
|
4
|
+
|
|
5
|
+
const domEnvironment: Environment = {
|
|
6
|
+
name: "test-story-dom",
|
|
7
|
+
async setup(global: typeof globalThis, { happyDOM = {} }) {
|
|
8
|
+
// Vitest loads environments outside the project's source resolution conditions.
|
|
9
|
+
const load = createRequire(import.meta.url);
|
|
10
|
+
const { documentTransport }: typeof import("#internal/documentTransport.ts") = load("#internal/documentTransport.ts");
|
|
11
|
+
const { installOutbound }: typeof import("#internal/routeOutbound.ts") = load("#internal/routeOutbound.ts");
|
|
12
|
+
const native = Object.fromEntries(
|
|
13
|
+
["fetch", "AbortController", "AbortSignal", "Headers", "Request", "Response"].map((name) => [name, Reflect.get(global, name)]),
|
|
14
|
+
);
|
|
15
|
+
const restoreOutbound = await installOutbound({}, true);
|
|
16
|
+
const win = new GlobalWindow({
|
|
17
|
+
...happyDOM,
|
|
18
|
+
console: global.console,
|
|
19
|
+
settings: { ...happyDOM.settings, disableErrorCapturing: true },
|
|
20
|
+
url: happyDOM.url ?? "http://localhost:3000",
|
|
21
|
+
});
|
|
22
|
+
win.happyDOM.settings.fetch.interceptor = documentTransport(global.fetch);
|
|
23
|
+
const { keys, originals } = populateGlobal(global, win, { bindFunctions: true });
|
|
24
|
+
for (const [name, value] of Object.entries(native)) {
|
|
25
|
+
Object.defineProperty(global, name, { configurable: true, value, writable: true });
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(global, "window", { configurable: true, value: win, writable: true });
|
|
28
|
+
Object.defineProperty(win.document, "defaultView", { configurable: true, get: () => win });
|
|
29
|
+
Object.defineProperty(global, Symbol.for("test-story.dom"), { configurable: true, value: true });
|
|
30
|
+
return {
|
|
31
|
+
async teardown(target) {
|
|
32
|
+
try {
|
|
33
|
+
await win.happyDOM.abort();
|
|
34
|
+
win.close();
|
|
35
|
+
} finally {
|
|
36
|
+
for (const key of keys) {
|
|
37
|
+
Reflect.deleteProperty(target, key);
|
|
38
|
+
}
|
|
39
|
+
for (const [key, descriptor] of originals) {
|
|
40
|
+
Object.defineProperty(target, key, descriptor);
|
|
41
|
+
}
|
|
42
|
+
Reflect.deleteProperty(target, Symbol.for("test-story.dom"));
|
|
43
|
+
restoreOutbound();
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
viteEnvironment: "client",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default domEnvironment;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function domFetch(baseUrl: string): typeof globalThis.fetch {
|
|
2
|
+
const base = new URL(baseUrl);
|
|
3
|
+
window.location.href = base.href;
|
|
4
|
+
const transport = window.fetch.bind(window);
|
|
5
|
+
return (input, init) => {
|
|
6
|
+
if (input instanceof Request) {
|
|
7
|
+
const request = new Request(input, init);
|
|
8
|
+
return transport(request.url, {
|
|
9
|
+
body: request.body,
|
|
10
|
+
cache: request.cache,
|
|
11
|
+
credentials: request.credentials,
|
|
12
|
+
headers: [...request.headers],
|
|
13
|
+
method: request.method,
|
|
14
|
+
mode: request.mode,
|
|
15
|
+
redirect: request.redirect,
|
|
16
|
+
referrer: request.referrer,
|
|
17
|
+
referrerPolicy: request.referrerPolicy,
|
|
18
|
+
signal: request.signal,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return transport(
|
|
22
|
+
typeof input === "string" ? new URL(input, base).href : input,
|
|
23
|
+
init?.headers instanceof Headers ? { ...init, headers: [...init.headers] } : init,
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export async function domNavigate(url: string | URL = window.location.href): Promise<Response> {
|
|
2
|
+
const response = await window.fetch(new URL(url, window.location.href), { credentials: "include" });
|
|
3
|
+
const html = await response.clone().text();
|
|
4
|
+
window.location.href = response.url;
|
|
5
|
+
document.open();
|
|
6
|
+
document.write(html);
|
|
7
|
+
document.close();
|
|
8
|
+
return response;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Request } from "happy-dom";
|
|
2
|
+
import type BrowserWindow from "happy-dom/lib/window/BrowserWindow.js";
|
|
3
|
+
|
|
4
|
+
export function documentRedirect(request: Request, window: BrowserWindow, target: URL, status: number, body: Buffer<ArrayBuffer> | null) {
|
|
5
|
+
const headers = new window.Headers(request.headers);
|
|
6
|
+
if (target.origin !== new URL(request.url).origin) {
|
|
7
|
+
for (const name of ["authorization", "cookie", "proxy-authorization"]) {
|
|
8
|
+
headers.delete(name);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const get = (status === 303 && request.method !== "HEAD") || ([301, 302].includes(status) && request.method === "POST");
|
|
12
|
+
if (get) {
|
|
13
|
+
headers.delete("content-type");
|
|
14
|
+
headers.delete("content-length");
|
|
15
|
+
}
|
|
16
|
+
const nextBody = get ? null : body;
|
|
17
|
+
return {
|
|
18
|
+
body: nextBody,
|
|
19
|
+
request: new window.Request(target.href, {
|
|
20
|
+
body: nextBody,
|
|
21
|
+
credentials: request.credentials,
|
|
22
|
+
headers,
|
|
23
|
+
method: get ? "GET" : request.method,
|
|
24
|
+
redirect: request.redirect,
|
|
25
|
+
signal: request.signal,
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Request } from "happy-dom";
|
|
2
|
+
|
|
3
|
+
export async function documentRequest(
|
|
4
|
+
fetch: typeof globalThis.fetch,
|
|
5
|
+
request: Request,
|
|
6
|
+
body: Buffer<ArrayBuffer> | null,
|
|
7
|
+
headers: Readonly<Record<string, string>>,
|
|
8
|
+
) {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
function abort() {
|
|
11
|
+
controller.abort(request.signal.reason);
|
|
12
|
+
}
|
|
13
|
+
function release() {
|
|
14
|
+
request.signal.removeEventListener("abort", abort);
|
|
15
|
+
}
|
|
16
|
+
if (request.signal.aborted) {
|
|
17
|
+
abort();
|
|
18
|
+
}
|
|
19
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
20
|
+
try {
|
|
21
|
+
return { release, response: await fetch(request.url, { body, headers, method: request.method, redirect: "manual", signal: controller.signal }) };
|
|
22
|
+
} catch (error) {
|
|
23
|
+
release();
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import type { Headers, IBrowserFrame, Request } from "happy-dom";
|
|
3
|
+
import FetchResponseHeaderUtility from "happy-dom/lib/fetch/utilities/FetchResponseHeaderUtility.js";
|
|
4
|
+
|
|
5
|
+
export function documentResponseHeaders(response: Response, request: Request, frame: IBrowserFrame) {
|
|
6
|
+
return FetchResponseHeaderUtility.parseResponseHeaders({
|
|
7
|
+
browserFrame: frame,
|
|
8
|
+
rawHeaders: [...response.headers]
|
|
9
|
+
.flatMap(([name, value]) => (name === "set-cookie" ? [] : [name, value]))
|
|
10
|
+
.concat(response.headers.getSetCookie().flatMap((cookie) => ["set-cookie", cookie])),
|
|
11
|
+
"requestURL": new URL(request.url),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function documentResponse(
|
|
16
|
+
response: Response,
|
|
17
|
+
request: Request,
|
|
18
|
+
frame: IBrowserFrame,
|
|
19
|
+
release: () => void,
|
|
20
|
+
redirected: boolean,
|
|
21
|
+
headers: Headers,
|
|
22
|
+
) {
|
|
23
|
+
if (response.body === null) {
|
|
24
|
+
release();
|
|
25
|
+
}
|
|
26
|
+
const result = new frame.window.Response(response.body === null ? null : Readable.toWeb(Readable.from(chunks(response.body, release))), {
|
|
27
|
+
headers,
|
|
28
|
+
status: response.status,
|
|
29
|
+
statusText: response.statusText,
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(result, "url", { value: request.url });
|
|
32
|
+
Object.defineProperty(result, "redirected", { value: redirected });
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function* chunks(stream: ReadableStream<Uint8Array>, release: () => void) {
|
|
37
|
+
const reader = stream.getReader();
|
|
38
|
+
try {
|
|
39
|
+
while (true) {
|
|
40
|
+
const { done, value } = await reader.read();
|
|
41
|
+
if (done) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
yield value;
|
|
45
|
+
}
|
|
46
|
+
} finally {
|
|
47
|
+
release();
|
|
48
|
+
await reader.cancel();
|
|
49
|
+
reader.releaseLock();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IFetchInterceptor } from "happy-dom";
|
|
2
|
+
import FetchRequestHeaderUtility from "happy-dom/lib/fetch/utilities/FetchRequestHeaderUtility.js";
|
|
3
|
+
import WindowBrowserContext from "happy-dom/lib/window/WindowBrowserContext.js";
|
|
4
|
+
import { documentRedirect } from "#internal/documentRedirect.ts";
|
|
5
|
+
import { documentRequest } from "#internal/documentRequest.ts";
|
|
6
|
+
import { documentResponse, documentResponseHeaders } from "#internal/documentResponse.ts";
|
|
7
|
+
|
|
8
|
+
export function documentTransport(fetch: typeof globalThis.fetch): IFetchInterceptor {
|
|
9
|
+
return {
|
|
10
|
+
async beforeAsyncRequest({ request, window }) {
|
|
11
|
+
const frame = new WindowBrowserContext(window).getBrowserFrame();
|
|
12
|
+
if (!frame) {
|
|
13
|
+
throw new Error("test-story cannot fetch after the document has closed");
|
|
14
|
+
}
|
|
15
|
+
let current = request;
|
|
16
|
+
let body = current.body === null ? null : Buffer.from(await current.arrayBuffer());
|
|
17
|
+
for (let redirects = 0; redirects <= 20; redirects += 1) {
|
|
18
|
+
const headers = FetchRequestHeaderUtility.getRequestHeaders({ browserFrame: frame, request: current, window });
|
|
19
|
+
delete headers.Connection;
|
|
20
|
+
const { release, response } = await documentRequest(fetch, current, body, headers);
|
|
21
|
+
const responseHeaders = documentResponseHeaders(response, current, frame);
|
|
22
|
+
const location = response.headers.get("location");
|
|
23
|
+
if (![301, 302, 303, 307, 308].includes(response.status) || location === null || current.redirect === "manual") {
|
|
24
|
+
return documentResponse(response, current, frame, release, redirects > 0, responseHeaders);
|
|
25
|
+
}
|
|
26
|
+
await response.body?.cancel();
|
|
27
|
+
release();
|
|
28
|
+
if (current.redirect === "error" || redirects === 20) {
|
|
29
|
+
throw new Error(`test-story cannot follow redirect from ${current.url}`);
|
|
30
|
+
}
|
|
31
|
+
const next = documentRedirect(current, window, new URL(location, current.url), response.status, body);
|
|
32
|
+
current = next.request;
|
|
33
|
+
body = next.body;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`test-story reached the redirect limit for ${request.url}`);
|
|
36
|
+
},
|
|
37
|
+
beforeSyncRequest({ request }) {
|
|
38
|
+
throw new Error(`test-story refuses synchronous document requests to ${new URL(request.url).hostname}; use fetch instead`);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { describeValue } from "#internal/render.ts";
|
|
2
2
|
import { explained } from "#internal/wrap.ts";
|
|
3
3
|
|
|
4
|
+
export function unrouted(hostname: string): Error {
|
|
5
|
+
return new Error(
|
|
6
|
+
explained(
|
|
7
|
+
`test-story refused an outbound request to ${hostname}`,
|
|
8
|
+
"Add this hostname to the story kit's outbound map and point it at a fake server. Only loopback requests may run without a route.",
|
|
9
|
+
),
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
export function refusal(name: string, line: string, cause: unknown): Error {
|
|
5
14
|
return new Error(
|
|
6
15
|
explained(
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { unrouted } from "#internal/errors.ts";
|
|
4
|
+
|
|
5
|
+
interface DispatchOptions {
|
|
6
|
+
readonly origin: string | URL;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Dispatcher {
|
|
10
|
+
readonly dispatch: (options: DispatchOptions, handler: unknown) => boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isDispatcher(value: unknown): value is Dispatcher {
|
|
14
|
+
return typeof value === "object" && value !== null && "dispatch" in value && typeof value.dispatch === "function";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function installOutbound(routes: Readonly<Record<string, string>>, strict = false) {
|
|
18
|
+
const origins = new Map(Object.entries(routes).map(([hostname, target]) => [hostname, new URL(target).origin]));
|
|
19
|
+
let refused: Error | undefined;
|
|
20
|
+
// A data URL initializes Node's bundled Undici without sending an HTTP request.
|
|
21
|
+
await fetch("data:,");
|
|
22
|
+
const slots = ["undici.globalDispatcher.1", "undici.globalDispatcher.2"].flatMap((name) => {
|
|
23
|
+
const key = Symbol.for(name);
|
|
24
|
+
const previous: unknown = Reflect.get(globalThis, key);
|
|
25
|
+
return isDispatcher(previous) ? [{ key, previous }] : [];
|
|
26
|
+
});
|
|
27
|
+
if (slots.length === 0) {
|
|
28
|
+
throw new Error("test-story outbound routing needs Node's built-in fetch dispatcher. Run the stories in Node.js 24 or newer.");
|
|
29
|
+
}
|
|
30
|
+
function restore() {
|
|
31
|
+
for (const { key, previous } of slots) {
|
|
32
|
+
Object.defineProperty(globalThis, key, { value: previous });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
for (const { key, previous } of slots) {
|
|
37
|
+
const router: Dispatcher = {
|
|
38
|
+
dispatch(options, handler) {
|
|
39
|
+
const hostname = new URL(options.origin).hostname;
|
|
40
|
+
const target = origins.get(hostname);
|
|
41
|
+
if (
|
|
42
|
+
strict
|
|
43
|
+
&& target === undefined
|
|
44
|
+
&& !["localhost", "[::1]"].includes(hostname)
|
|
45
|
+
&& !(isIP(hostname) === 4 && hostname.startsWith("127."))
|
|
46
|
+
) {
|
|
47
|
+
refused = unrouted(hostname);
|
|
48
|
+
throw refused;
|
|
49
|
+
}
|
|
50
|
+
return previous.dispatch(target === undefined ? options : { ...options, origin: target }, handler);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(globalThis, key, { value: router });
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
restore();
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
restore();
|
|
61
|
+
if (refused !== undefined) {
|
|
62
|
+
throw refused;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
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
|
+
}
|
|
@@ -25,6 +25,7 @@ export interface LooseDefinition {
|
|
|
25
25
|
readonly inspect?: (engine: unknown) => unknown;
|
|
26
26
|
readonly layer?: AnyTestLayer;
|
|
27
27
|
readonly name: string;
|
|
28
|
+
readonly outbound?: Readonly<Record<string, string>>;
|
|
28
29
|
readonly run?: LooseRunHooks<unknown>;
|
|
29
30
|
readonly stages: readonly string[];
|
|
30
31
|
readonly verbs: (engine: unknown, story: LooseStory) => object;
|
|
@@ -4,6 +4,7 @@ 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";
|
|
7
8
|
import type { LooseDefinition, LooseTrait } from "#internal/seed.ts";
|
|
8
9
|
import { start } from "#internal/start.ts";
|
|
9
10
|
|
|
@@ -52,8 +53,16 @@ function declaring(definition: LooseDefinition, genre: Genre, declare: EffectTes
|
|
|
52
53
|
|
|
53
54
|
export function storyIt(definition: LooseDefinition): LooseIt {
|
|
54
55
|
const genre = genreOf(definition.name);
|
|
56
|
+
const outbound = definition.outbound ?? (Reflect.get(globalThis, Symbol.for("test-story.dom")) ? {} : undefined);
|
|
55
57
|
const { effectApp } = makeEffectIt({
|
|
56
|
-
around:
|
|
58
|
+
around:
|
|
59
|
+
outbound === undefined
|
|
60
|
+
? definition.around
|
|
61
|
+
: (effect) =>
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
yield* routeOutbound(outbound);
|
|
64
|
+
return yield* definition.around?.(effect) ?? effect;
|
|
65
|
+
}),
|
|
57
66
|
clock: definition.clock,
|
|
58
67
|
layer: definition.layer ?? Layer.empty,
|
|
59
68
|
makeHarness: () => Effect.void,
|
package/src/server/storyKit.ts
CHANGED
|
@@ -52,6 +52,7 @@ export interface StoryKitDefinition<
|
|
|
52
52
|
readonly inspect?: (engine: TEngine) => unknown;
|
|
53
53
|
readonly layer?: Layer.Layer<TProvided, unknown, never>;
|
|
54
54
|
readonly name: string;
|
|
55
|
+
readonly outbound?: Readonly<Record<string, string>>;
|
|
55
56
|
readonly run?: RunHooks<TEngine, TStep, TFailed, TDiagnose>;
|
|
56
57
|
readonly stages: readonly [TStage, ...TStage[]];
|
|
57
58
|
readonly verbs: (engine: TEngine, story: Story<TEngine, TStep | TFailed | TDiagnose>) => TVerbs;
|