@wcstack/testing 4.10.1 → 4.11.2

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/README.ja.md CHANGED
@@ -47,6 +47,8 @@ npm install -D @wcstack/testing @wcstack/state @wcstack/server vitest happy-dom
47
47
  | `maxIterations` | 待機中に挿入された要素を拾う安定化ループの回数(既定 10) |
48
48
  | `resolveModule` | 4.9 から。`(url: URL) => string \| URL \| null` — `<wcs-state src>` のモジュールを読むローカルのファイル。`@wcstack/server` の `renderToString` と同じオプションで、ページが名指す URL(`document.baseURI` で解決したもの — happy-dom の下では `http:` の URL)を受け取り、パスか `file:` URL を返す。Node のモジュールローダは `file:` と `data:` の URL しか読まない。mount の間だけ置かれ、モジュールは mount ごとに読み直すので、テストの間で状態のオブジェクトは共有されない。これが無いか何も返さないと、状態の初期化はそう言って失敗する — ルートの `<wcs-state>` なら `mount()` がそれで reject する。`.json` の `src` は従来どおり fetch で読む。[自分のファイルにある状態](#自分のファイルにある状態) を参照 |
49
49
 
50
+ 4.11 から、`mount()` が持たないオプションや、オプションの型と違う値は、文書に触れる前にその場で reject します — どのパッケージの `bootstrapXxx()` とも同じく、オプションの名前を言い、オプションの一覧を添えます。綴り違いの `resolveModul` は無視され、テストは後で Node の `ERR_UNSUPPORTED_ESM_URL_SCHEME` で落ちていました。値が `undefined` のオプションは飛ばします。`fire()` の `init` と `installDom()` のオプションも同じに検査します。
51
+
50
52
  返る `MountedApp`:
51
53
 
52
54
  | メンバー | 説明 |
@@ -90,7 +92,7 @@ const app = await mount(`
90
92
 
91
93
  ### `fire(target, type, init?) → boolean`
92
94
 
93
- `Event`(`init.detail` があれば `CustomEvent`)を既定で bubbling させて dispatch します。ハンドラが `preventDefault()` を呼ぶと `false`。
95
+ `Event`(`init.detail` があれば `CustomEvent`)を既定で bubbling させて dispatch します。ハンドラが `preventDefault()` を呼ぶと `false`。4.11 から、`init` の `bubbles`・`cancelable`・`composed`・`detail` 以外のキーは throw します: イベントに届いていませんでした — `{ key: "Enter" }` は `KeyboardEvent` を作らない。別のイベントクラスは `target.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }))` と自分で送ってください。
94
96
 
95
97
  ### `installDom(options?) → Promise<() => Promise<void>>`
96
98
 
package/README.md CHANGED
@@ -47,6 +47,8 @@ Registers the elements, inserts `html`, and resolves once everything under the r
47
47
  | `maxIterations` | Stabilization rounds for elements inserted while waiting (default 10) |
48
48
  | `resolveModule` | Since 4.9. `(url: URL) => string \| URL \| null` — the local file a `<wcs-state src>` module is read from, the same option as `@wcstack/server`'s `renderToString`: it gets the URL the page names (resolved against `document.baseURI` — an `http:` URL under happy-dom) and returns a path or a `file:` URL. Node's module loader reads `file:` and `data:` URLs only. Installed for the length of the mount; the module is imported anew for each mount, so tests do not share a state object. Without it, or when it returns nothing, the state fails to initialize with a message saying so — for the root `<wcs-state>`, `mount()` rejects with it. A `.json` `src` is fetched, as before. See [A state in its own file](#a-state-in-its-own-file) |
49
49
 
50
+ Since 4.11, an option `mount()` does not have, or a value not of the option's type, rejects at once — before the document is touched — naming the option and listing the options, as every package's `bootstrapXxx()` does; a misspelt `resolveModul` used to be ignored, and the test failed later with Node's `ERR_UNSUPPORTED_ESM_URL_SCHEME`. An option whose value is `undefined` is skipped. `fire()`'s `init` and `installDom()`'s options are checked the same way.
51
+
50
52
  The returned `MountedApp`:
51
53
 
52
54
  | Member | Description |
@@ -90,7 +92,7 @@ Two microtask turns and one macrotask — enough for a write to reach the DOM. U
90
92
 
91
93
  ### `fire(target, type, init?) → boolean`
92
94
 
93
- Dispatches an `Event` (or a `CustomEvent` when `init.detail` is given), bubbling by default. Returns `false` when a handler called `preventDefault()`.
95
+ Dispatches an `Event` (or a `CustomEvent` when `init.detail` is given), bubbling by default. Returns `false` when a handler called `preventDefault()`. Since 4.11, an `init` key other than `bubbles`, `cancelable`, `composed` and `detail` throws: it never reached the event — `{ key: "Enter" }` made no `KeyboardEvent`. For another event class, call `target.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }))` yourself.
94
96
 
95
97
  ### `installDom(options?) → Promise<() => Promise<void>>`
96
98
 
package/dist/index.d.ts CHANGED
@@ -79,6 +79,8 @@ declare function settle(): Promise<void>;
79
79
  *
80
80
  * Returns what `dispatchEvent` returns (`false` when a handler called
81
81
  * `preventDefault()`). Follow with `await settle()` before asserting the DOM.
82
+ * An `init` key other than `bubbles` / `cancelable` / `composed` / `detail` throws:
83
+ * it would not reach the event (`{ key: "Enter" }` makes no `KeyboardEvent`).
82
84
  */
83
85
  declare function fire(target: EventTarget, type: string, init?: EventInit & {
84
86
  detail?: unknown;
package/dist/index.esm.js CHANGED
@@ -1,6 +1,33 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import { waitForReady, installGlobals } from '@wcstack/server';
3
3
 
4
+ const isString = (value) => typeof value === "string";
5
+ const isNumber = (value) => typeof value === "number";
6
+ const isBoolean = (value) => typeof value === "boolean";
7
+ const isFunction = (value) => typeof value === "function";
8
+ const isObject = (value) => typeof value === "object" && value !== null;
9
+ const isAny = () => true;
10
+ /**
11
+ * Refuses an option the helper does not have, or a value not of the option's type, before the helper
12
+ * does anything — as every package's `bootstrapXxx()` does. A misspelt option did nothing:
13
+ * `mount(html, { resolveModul })` failed later with Node's `ERR_UNSUPPORTED_ESM_URL_SCHEME`, far from
14
+ * the typo (docs/seam-apps.md, homeroom). An option whose value is `undefined` is skipped.
15
+ */
16
+ function checkOptions(helper, options, spec, hint = "") {
17
+ const list = () => Object.entries(spec).map(([name, [, type]]) => `${name} (${type})`).join(", ");
18
+ if (!isObject(options) || Array.isArray(options)) {
19
+ throw new Error(`@wcstack/testing: ${helper}: its options must be an object. Its options: ${list()}.`);
20
+ }
21
+ for (const [name, value] of Object.entries(options)) {
22
+ if (value === undefined)
23
+ continue;
24
+ const entry = Object.hasOwn(spec, name) ? spec[name] : undefined;
25
+ if (entry === undefined || !entry[0](value)) {
26
+ throw new Error(`@wcstack/testing: ${helper}: "${name}" is not one of its options, or not of the option's type. Its options: ${list()}.${hint}`);
27
+ }
28
+ }
29
+ }
30
+
4
31
  /**
5
32
  * mount() — the README recipe ("Testing Your Page", @wcstack/state) as one call:
6
33
  * register the elements, insert the HTML, wait for every element and binding
@@ -138,7 +165,16 @@ async function defaultBootstraps() {
138
165
  const { bootstrapState } = await import('@wcstack/state');
139
166
  return [bootstrapState];
140
167
  }
168
+ const MOUNT_OPTIONS = {
169
+ root: [(value) => value === "document" || value === "shadow", `"document" or "shadow"`],
170
+ bootstrap: [(value) => Array.isArray(value) && value.every(isFunction), "an array of functions"],
171
+ stateTagName: [isString, "a string"],
172
+ maxIterations: [isNumber, "a number"],
173
+ resolveModule: [isFunction, "a function"],
174
+ };
141
175
  async function mount(html, options = {}) {
176
+ // before anything touches the document: a misspelt option used to be ignored
177
+ checkOptions("mount()", options, MOUNT_OPTIONS);
142
178
  const stateTagName = options.stateTagName ?? "wcs-state";
143
179
  const waiting = findWaitingState(html, stateTagName);
144
180
  if (waiting !== undefined) {
@@ -229,14 +265,23 @@ async function settle() {
229
265
  await new Promise((resolve) => setTimeout(resolve, 0));
230
266
  }
231
267
 
268
+ const FIRE_INIT = {
269
+ bubbles: [isBoolean, "a boolean"],
270
+ cancelable: [isBoolean, "a boolean"],
271
+ composed: [isBoolean, "a boolean"],
272
+ detail: [isAny, "any value"],
273
+ };
232
274
  /**
233
275
  * Dispatch a DOM event the way a user action would: bubbling by default, a
234
276
  * `CustomEvent` when `detail` is given, a plain `Event` otherwise.
235
277
  *
236
278
  * Returns what `dispatchEvent` returns (`false` when a handler called
237
279
  * `preventDefault()`). Follow with `await settle()` before asserting the DOM.
280
+ * An `init` key other than `bubbles` / `cancelable` / `composed` / `detail` throws:
281
+ * it would not reach the event (`{ key: "Enter" }` makes no `KeyboardEvent`).
238
282
  */
239
283
  function fire(target, type, init = {}) {
284
+ checkOptions("fire()", init, FIRE_INIT, " fire() dispatches an Event, or a CustomEvent with detail; for another event class (KeyboardEvent, InputEvent, …) call target.dispatchEvent() yourself.");
240
285
  const { detail, ...eventInit } = init;
241
286
  const event = detail !== undefined
242
287
  ? new CustomEvent(type, { bubbles: true, ...eventInit, detail })
@@ -257,7 +302,12 @@ function fire(target, type, init = {}) {
257
302
  * `happy-dom` is an optional peer: it is only needed here. A `window` can also be
258
303
  * passed in (`installDom({ window })`) to skip the import entirely.
259
304
  */
305
+ const INSTALL_DOM_OPTIONS = {
306
+ url: [isString, "a string"],
307
+ window: [isObject, "a happy-dom Window"],
308
+ };
260
309
  async function installDom(options = {}) {
310
+ checkOptions("installDom()", options, INSTALL_DOM_OPTIONS);
261
311
  const window = options.window ?? (await createWindow(options.url ?? "http://localhost/"));
262
312
  const restore = installGlobals(window);
263
313
  return async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../../src/mount.ts","../../src/settle.ts","../../src/fire.ts","../../src/installDom.ts"],"sourcesContent":["/**\n * mount() — the README recipe (\"Testing Your Page\", @wcstack/state) as one call:\n * register the elements, insert the HTML, wait for every element and binding\n * under it, and hand back typed accessors for the page's `<wcs-state>`s.\n *\n * The wait is `@wcstack/server`'s `waitForReady` — the same stabilization loop\n * `renderToString` performs, so `<wcs-router>`'s first route, a `$connectedCallback`\n * that inserts more elements, and `<wcs-state>`'s binding construction are all\n * covered by the one call (docs/app-testing-and-typescript-impl-plan.md D11).\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport { waitForReady } from \"@wcstack/server\";\n\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface MountOptions {\n /**\n * Where to insert the HTML. `\"document\"` (default) replaces `document.body`'s\n * content; `\"shadow\"` puts it inside an open ShadowRoot on a fresh host element\n * appended to `document.body` (bindings then scope to that root).\n */\n readonly root?: \"document\" | \"shadow\";\n /**\n * Element registrations to run before inserting the HTML. Default:\n * `[bootstrapState]` imported lazily from `@wcstack/state`. Add the packages\n * the page uses — `bootstrapRouter`, `bootstrapFetch`, … — as functions or\n * async loaders (`async () => (await import(\"@wcstack/router\")).bootstrapRouter()`);\n * every wcstack bootstrap is idempotent, so calling them per test is safe.\n */\n readonly bootstrap?: readonly BootstrapFunction[];\n /** Tag name of the state element when `bootstrapState({ tagNames })` renamed it (default `wcs-state`). */\n readonly stateTagName?: string;\n /** Passed to `waitForReady` (default 10 stabilization rounds). */\n readonly maxIterations?: number;\n /**\n * The local file a `<wcs-state src>` module is read from. It gets the URL the page names (resolved\n * against `document.baseURI`, an `http:` URL under happy-dom, which Node's module loader does not\n * read — it reads `file:` and `data:` URLs only) and returns a path or a `file:` URL, e.g.\n * `(url) => new URL(\".\" + url.pathname, import.meta.url)`. The same contract as\n * `renderToString({ resolveModule })` (`@wcstack/server`). Installed for the length of the mount;\n * each mount imports the module anew, so tests do not share a state object. Without it — or when it\n * returns nothing — a `src` module state fails to initialize with a message saying so. A `.json`\n * `src` is fetched, as before.\n */\n readonly resolveModule?: (url: URL) => string | URL | null | undefined;\n}\n\nexport interface StateHandle {\n /** The `<wcs-state>` element itself. */\n readonly element: HTMLElement;\n /** Read through a readonly proxy — the value `fn` returns is passed back. */\n read<T>(fn: (state: any) => T): T;\n /** Write through a writable proxy, exactly as a handler does. Follow with `await settle()`. */\n write(fn: (state: any) => void | Promise<void>): Promise<void>;\n}\n\nexport interface MountedApp {\n /** The root the HTML lives under: `document` or the ShadowRoot. Query it. */\n readonly root: Document | ShadowRoot;\n /** The node whose children are the mounted HTML: `document.body` or the shadow host. */\n readonly container: Element;\n /**\n * Accessor for the root `<wcs-state>` (v2: one state tree per root — volumes\n * (`mount=`) and `bind-component` elements are not it). Throws if absent.\n */\n state(): StateHandle;\n /** Remove the mounted HTML. */\n unmount(): void;\n}\n\ninterface StateElementLike extends HTMLElement {\n createState(mutability: \"readonly\" | \"writable\", callback: (state: any) => void): void;\n createStateAsync(mutability: \"readonly\" | \"writable\", callback: (state: any) => Promise<void>): Promise<void>;\n}\n\nlet inlineScriptLoaderPatched = false;\n\n/**\n * Route `<wcs-state>` inline `<script type=\"module\">` through the `data:` URL\n * loader. Node cannot import `blob:` URLs, so the browser path (`URL.createObjectURL`)\n * would leave an inline-script state pending forever; the loader falls back to a\n * `data:` URL when `createObjectURL` is absent — the same switch `@wcstack/server`\n * flips for SSR. Applied once per process, only when a browser-style\n * `createObjectURL` is present (i.e. under vitest's happy-dom environment).\n */\nfunction patchInlineScriptLoader(): void {\n if (inlineScriptLoaderPatched) return;\n inlineScriptLoaderPatched = true;\n if (typeof URL.createObjectURL === \"function\") {\n (URL as unknown as { createObjectURL: unknown }).createObjectURL = undefined;\n }\n}\n\nconst TEXT_SETTER_PATCHED = Symbol.for(\"wcstack.testing.textSetterPatched\");\n\n/**\n * happy-dom's `textContent` setter treats a numeric `0` as empty (and `innerText`\n * throws on any non-string), whereas browsers stringify: `el.textContent = 0`\n * renders \"0\". A `textContent: count` binding assigns the raw number, so a count\n * that reaches zero would vanish only under happy-dom. Wrap the setters on every\n * prototype that owns one (Node, Element, HTMLElement) to coerce the way the\n * DOM spec does (`null` → \"\", anything else → `String(value)`). Idempotent per\n * prototype, so a fresh window from `installDom()` gets patched too.\n */\nfunction patchTextSetters(): void {\n const targets: Array<[unknown, string]> = [\n [globalThis.Node, \"textContent\"],\n [globalThis.Element, \"textContent\"],\n [globalThis.HTMLElement, \"textContent\"],\n [globalThis.HTMLElement, \"innerText\"],\n ];\n for (const [ctor, property] of targets) {\n const proto = (ctor as { prototype?: object } | undefined)?.prototype as (Record<symbol, unknown> & object) | undefined;\n if (proto === undefined) continue;\n const marker = `${String(TEXT_SETTER_PATCHED)}:${property}`;\n // own-property check: Element.prototype inherits Node.prototype's marker, and an\n // inherited marker must not make the Element setter (the one happy-dom uses) skip.\n if (Object.prototype.hasOwnProperty.call(proto, marker)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(proto, property);\n if (descriptor?.set === undefined) continue;\n const originalSet = descriptor.set;\n Object.defineProperty(proto, property, {\n ...descriptor,\n set(this: unknown, value: unknown) {\n originalSet.call(this, value === null || value === undefined ? \"\" : String(value));\n },\n });\n Object.defineProperty(proto, marker, { value: true, enumerable: false, configurable: true });\n }\n}\n\n/**\n * The global key a renderer installs its module resolver under, for `<wcs-state src>` (the\n * ssr-snapshot protocol's `SSR_MODULE_RESOLVER_KEY`, /protocol/ssr-snapshot.ts): `Symbol.for`, so\n * `@wcstack/state` reads the same key without either package importing the other's copy.\n */\nconst MODULE_RESOLVER_KEY = Symbol.for(\"wcstack.ssr.resolveModule\");\n\n/** The number of the mount (each mount's `src` modules are its own). */\nlet mounts = 0;\n\n/**\n * Puts a resolver for `<wcs-state src>` modules on the global key for the length of one mount, as\n * `renderToString` does for a render: `file:` / `data:` are imported as they are, any other URL from\n * the file `resolve` returns — with the mount's number in its query, since Node keeps a module per URL\n * (without it, every mount would get the first mount's state object). Without `resolve`, or when it\n * returns nothing, it throws saying what to do (Node's own error was `ERR_UNSUPPORTED_ESM_URL_SCHEME`).\n * Returns the function that puts back what was there.\n */\nfunction installModuleResolver(resolve: MountOptions[\"resolveModule\"]): () => void {\n const g = globalThis as Record<symbol, unknown>;\n const had = Object.prototype.hasOwnProperty.call(g, MODULE_RESOLVER_KEY);\n const previous = g[MODULE_RESOLVER_KEY];\n const n = ++mounts;\n g[MODULE_RESOLVER_KEY] = (url: string): string | null => {\n const u = new URL(url);\n if (u.protocol === \"file:\" || u.protocol === \"data:\") return null;\n const local = resolve?.(u);\n if (local === null || local === undefined) {\n throw new Error(\n `@wcstack/testing: <wcs-state src> names ${url}, which Node cannot import (its loader reads file: and data: URLs). ` +\n (resolve === undefined\n ? `Pass mount(html, { resolveModule }) to map it to a local file — e.g. resolveModule: (url) => new URL(\".\" + url.pathname, import.meta.url) — or write the state in the HTML (json=, or an inline <script type=\"module\">).`\n : \"resolveModule returned no file for it.\"),\n );\n }\n const file = local instanceof URL ? new URL(local.href) : /^file:/i.test(local) ? new URL(local) : pathToFileURL(local);\n file.searchParams.set(\"wcs-mount\", String(n));\n return file.href;\n };\n return () => {\n if (had) g[MODULE_RESOLVER_KEY] = previous;\n else delete g[MODULE_RESOLVER_KEY];\n };\n}\n\n/**\n * A `<wcs-state>` in `html` that would wait for `setInitialState()`: none of `state` / `src` / `json`\n * and no inline `<script type=\"module\">` (the element's resolution order, @wcstack/state README \"State\n * Initialization\"). A test cannot call `setInitialState()` before `mount()` returns, so `mount()` would\n * never resolve — it used to time out without a word. A root or a volume (`mount=`); not a\n * `bind-component` element (its state is its host's property), and not one inside a `<template>`\n * (template content is not in the page: a `for:` / `if:` row, a route, a DCC definition).\n */\nfunction findWaitingState(html: string, stateTagName: string): Element | undefined {\n const probe = document.createElement(\"template\");\n probe.innerHTML = html;\n return [...probe.content.querySelectorAll(stateTagName)].find((el) =>\n !el.hasAttribute(\"bind-component\") &&\n !el.hasAttribute(\"state\") &&\n !el.hasAttribute(\"src\") &&\n !el.hasAttribute(\"json\") &&\n el.querySelector('script[type=\"module\"]') === null);\n}\n\n/** `<wcs-state mount=\"cfg\">` — the element as written (its start tag), for a message. */\nfunction startTag(el: Element): string {\n const attributes = [...el.attributes].map((a) => (a.value === \"\" ? ` ${a.name}` : ` ${a.name}=\"${a.value}\"`));\n return `<${el.localName}${attributes.join(\"\")}>`;\n}\n\nasync function defaultBootstraps(): Promise<readonly BootstrapFunction[]> {\n // Lazy: the element classes bind their base class at module evaluation, which\n // must happen after the DOM globals exist (installDom() in bare Node).\n const { bootstrapState } = await import(\"@wcstack/state\");\n return [bootstrapState];\n}\n\nexport async function mount(html: string, options: MountOptions = {}): Promise<MountedApp> {\n const stateTagName = options.stateTagName ?? \"wcs-state\";\n const waiting = findWaitingState(html, stateTagName);\n if (waiting !== undefined) {\n throw new Error(\n `@wcstack/testing: ${startTag(waiting)} has no state to load (no state, src or json attribute, and no inline <script type=\"module\">), ` +\n `so it waits for setInitialState() — which a test cannot call before mount() returns: mount() would never resolve. ` +\n `Give it its state in the HTML: json='{\"count\": 0}', an inline <script type=\"module\">export default { … }</script>, ` +\n `or src=\"./state.js\" with mount(html, { resolveModule }).`,\n );\n }\n\n patchInlineScriptLoader();\n patchTextSetters();\n\n for (const bootstrap of options.bootstrap ?? (await defaultBootstraps())) {\n await bootstrap();\n }\n\n let root: Document | ShadowRoot;\n let container: Element;\n const restoreResolver = installModuleResolver(options.resolveModule);\n try {\n if (options.root === \"shadow\") {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-wcs-testing-host\", \"\");\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = html;\n document.body.appendChild(host);\n root = shadow;\n container = host;\n } else {\n document.body.innerHTML = html;\n root = document;\n container = document.body;\n }\n\n await waitForReady(root, { maxIterations: options.maxIterations });\n } finally {\n restoreResolver();\n }\n\n return {\n root,\n container,\n state(...legacyArgs: never[]): StateHandle {\n if (legacyArgs.length > 0) {\n throw new Error(\n `@wcstack/testing: state(name) was removed in v2 — there is one state tree per root. ` +\n `Mount the named state onto the tree (<wcs-state mount=\"...\">) and read it by its path prefix via state().`,\n );\n }\n const element = [...root.querySelectorAll<StateElementLike>(stateTagName)]\n .find((el) => !el.hasAttribute(\"mount\") && !el.hasAttribute(\"bind-component\"));\n if (element === undefined) {\n throw new Error(`@wcstack/testing: no <${stateTagName}> under the mounted root`);\n }\n return {\n element,\n read(fn) {\n let out!: ReturnType<typeof fn>;\n element.createState(\"readonly\", (state) => {\n out = fn(state);\n });\n return out;\n },\n async write(fn) {\n await element.createStateAsync(\"writable\", async (state) => {\n await fn(state);\n });\n },\n };\n },\n unmount() {\n if (root === document) {\n document.body.innerHTML = \"\";\n } else {\n container.remove();\n }\n },\n };\n}\n","/**\n * Let a state write reach the DOM.\n *\n * `@wcstack/state` applies updates on the microtask queue; two microtask turns\n * cover the write → apply chain, and one macrotask (`setTimeout(0)`) covers\n * anything a binding defers (e.g. a `customElements.whenDefined` re-apply).\n * This is exactly the wait the README recipe uses, fixed in one place.\n */\nexport async function settle(): Promise<void> {\n await Promise.resolve();\n await Promise.resolve();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n","/**\n * Dispatch a DOM event the way a user action would: bubbling by default, a\n * `CustomEvent` when `detail` is given, a plain `Event` otherwise.\n *\n * Returns what `dispatchEvent` returns (`false` when a handler called\n * `preventDefault()`). Follow with `await settle()` before asserting the DOM.\n */\nexport function fire(target: EventTarget, type: string, init: EventInit & { detail?: unknown } = {}): boolean {\n const { detail, ...eventInit } = init;\n const event = detail !== undefined\n ? new CustomEvent(type, { bubbles: true, ...eventInit, detail })\n : new Event(type, { bubbles: true, ...eventInit });\n return target.dispatchEvent(event);\n}\n","/**\n * Bare Node (no vitest `environment: 'happy-dom'`): create a happy-dom `Window`\n * and install its globals, using the same `installGlobals` `@wcstack/server` runs\n * for SSR. Returns an async restore function that also closes the window.\n *\n * Import order matters: `@wcstack/state`'s element classes pick their base class\n * when the module is evaluated, so `mount()` imports it lazily — after this\n * function has installed `HTMLElement`. Do the same for any other wcstack\n * package you bootstrap (`async () => (await import(\"@wcstack/router\")).bootstrapRouter()`).\n *\n * `happy-dom` is an optional peer: it is only needed here. A `window` can also be\n * passed in (`installDom({ window })`) to skip the import entirely.\n */\n\nimport { installGlobals } from \"@wcstack/server\";\n\nexport interface InstallDomOptions {\n /** `window.location` for the page (default `http://localhost/`). */\n readonly url?: string;\n /** A ready-made happy-dom `Window`; when given, `happy-dom` is not imported. */\n readonly window?: unknown;\n}\n\ninterface HappyDomWindowLike {\n readonly happyDOM: { close(): Promise<void> };\n}\n\nexport async function installDom(options: InstallDomOptions = {}): Promise<() => Promise<void>> {\n const window = options.window ?? (await createWindow(options.url ?? \"http://localhost/\"));\n const restore = installGlobals(window as Parameters<typeof installGlobals>[0]);\n return async () => {\n restore();\n await (window as HappyDomWindowLike).happyDOM.close();\n };\n}\n\nasync function createWindow(url: string): Promise<unknown> {\n let mod: { Window: new (options: { url: string }) => unknown };\n try {\n mod = (await import(\"happy-dom\")) as typeof mod;\n } catch {\n throw new Error(\n \"@wcstack/testing: installDom() needs happy-dom (npm i -D happy-dom), or pass { window } — under vitest with environment: 'happy-dom' it is not needed at all\",\n );\n }\n return new mod.Window({ url });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../../src/options.ts","../../src/mount.ts","../../src/settle.ts","../../src/fire.ts","../../src/installDom.ts"],"sourcesContent":["/**\n * The options a helper takes: for each name, the test of its value and the type the message names.\n */\nexport type OptionSpec = Readonly<Record<string, readonly [test: (value: unknown) => boolean, type: string]>>;\n\nexport const isString = (value: unknown): boolean => typeof value === \"string\";\nexport const isNumber = (value: unknown): boolean => typeof value === \"number\";\nexport const isBoolean = (value: unknown): boolean => typeof value === \"boolean\";\nexport const isFunction = (value: unknown): boolean => typeof value === \"function\";\nexport const isObject = (value: unknown): boolean => typeof value === \"object\" && value !== null;\nexport const isAny = (): boolean => true;\n\n/**\n * Refuses an option the helper does not have, or a value not of the option's type, before the helper\n * does anything — as every package's `bootstrapXxx()` does. A misspelt option did nothing:\n * `mount(html, { resolveModul })` failed later with Node's `ERR_UNSUPPORTED_ESM_URL_SCHEME`, far from\n * the typo (docs/seam-apps.md, homeroom). An option whose value is `undefined` is skipped.\n */\nexport function checkOptions(helper: string, options: unknown, spec: OptionSpec, hint = \"\"): void {\n const list = (): string => Object.entries(spec).map(([name, [, type]]) => `${name} (${type})`).join(\", \");\n if (!isObject(options) || Array.isArray(options)) {\n throw new Error(`@wcstack/testing: ${helper}: its options must be an object. Its options: ${list()}.`);\n }\n for (const [name, value] of Object.entries(options as object)) {\n if (value === undefined) continue;\n const entry = Object.hasOwn(spec, name) ? spec[name] : undefined;\n if (entry === undefined || !entry[0](value)) {\n throw new Error(\n `@wcstack/testing: ${helper}: \"${name}\" is not one of its options, or not of the option's type. Its options: ${list()}.${hint}`,\n );\n }\n }\n}\n","/**\n * mount() — the README recipe (\"Testing Your Page\", @wcstack/state) as one call:\n * register the elements, insert the HTML, wait for every element and binding\n * under it, and hand back typed accessors for the page's `<wcs-state>`s.\n *\n * The wait is `@wcstack/server`'s `waitForReady` — the same stabilization loop\n * `renderToString` performs, so `<wcs-router>`'s first route, a `$connectedCallback`\n * that inserts more elements, and `<wcs-state>`'s binding construction are all\n * covered by the one call (docs/app-testing-and-typescript-impl-plan.md D11).\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport { waitForReady } from \"@wcstack/server\";\nimport { checkOptions, isFunction, isNumber, isString, type OptionSpec } from \"./options.js\";\n\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface MountOptions {\n /**\n * Where to insert the HTML. `\"document\"` (default) replaces `document.body`'s\n * content; `\"shadow\"` puts it inside an open ShadowRoot on a fresh host element\n * appended to `document.body` (bindings then scope to that root).\n */\n readonly root?: \"document\" | \"shadow\";\n /**\n * Element registrations to run before inserting the HTML. Default:\n * `[bootstrapState]` imported lazily from `@wcstack/state`. Add the packages\n * the page uses — `bootstrapRouter`, `bootstrapFetch`, … — as functions or\n * async loaders (`async () => (await import(\"@wcstack/router\")).bootstrapRouter()`);\n * every wcstack bootstrap is idempotent, so calling them per test is safe.\n */\n readonly bootstrap?: readonly BootstrapFunction[];\n /** Tag name of the state element when `bootstrapState({ tagNames })` renamed it (default `wcs-state`). */\n readonly stateTagName?: string;\n /** Passed to `waitForReady` (default 10 stabilization rounds). */\n readonly maxIterations?: number;\n /**\n * The local file a `<wcs-state src>` module is read from. It gets the URL the page names (resolved\n * against `document.baseURI`, an `http:` URL under happy-dom, which Node's module loader does not\n * read — it reads `file:` and `data:` URLs only) and returns a path or a `file:` URL, e.g.\n * `(url) => new URL(\".\" + url.pathname, import.meta.url)`. The same contract as\n * `renderToString({ resolveModule })` (`@wcstack/server`). Installed for the length of the mount;\n * each mount imports the module anew, so tests do not share a state object. Without it — or when it\n * returns nothing — a `src` module state fails to initialize with a message saying so. A `.json`\n * `src` is fetched, as before.\n */\n readonly resolveModule?: (url: URL) => string | URL | null | undefined;\n}\n\nexport interface StateHandle {\n /** The `<wcs-state>` element itself. */\n readonly element: HTMLElement;\n /** Read through a readonly proxy — the value `fn` returns is passed back. */\n read<T>(fn: (state: any) => T): T;\n /** Write through a writable proxy, exactly as a handler does. Follow with `await settle()`. */\n write(fn: (state: any) => void | Promise<void>): Promise<void>;\n}\n\nexport interface MountedApp {\n /** The root the HTML lives under: `document` or the ShadowRoot. Query it. */\n readonly root: Document | ShadowRoot;\n /** The node whose children are the mounted HTML: `document.body` or the shadow host. */\n readonly container: Element;\n /**\n * Accessor for the root `<wcs-state>` (v2: one state tree per root — volumes\n * (`mount=`) and `bind-component` elements are not it). Throws if absent.\n */\n state(): StateHandle;\n /** Remove the mounted HTML. */\n unmount(): void;\n}\n\ninterface StateElementLike extends HTMLElement {\n createState(mutability: \"readonly\" | \"writable\", callback: (state: any) => void): void;\n createStateAsync(mutability: \"readonly\" | \"writable\", callback: (state: any) => Promise<void>): Promise<void>;\n}\n\nlet inlineScriptLoaderPatched = false;\n\n/**\n * Route `<wcs-state>` inline `<script type=\"module\">` through the `data:` URL\n * loader. Node cannot import `blob:` URLs, so the browser path (`URL.createObjectURL`)\n * would leave an inline-script state pending forever; the loader falls back to a\n * `data:` URL when `createObjectURL` is absent — the same switch `@wcstack/server`\n * flips for SSR. Applied once per process, only when a browser-style\n * `createObjectURL` is present (i.e. under vitest's happy-dom environment).\n */\nfunction patchInlineScriptLoader(): void {\n if (inlineScriptLoaderPatched) return;\n inlineScriptLoaderPatched = true;\n if (typeof URL.createObjectURL === \"function\") {\n (URL as unknown as { createObjectURL: unknown }).createObjectURL = undefined;\n }\n}\n\nconst TEXT_SETTER_PATCHED = Symbol.for(\"wcstack.testing.textSetterPatched\");\n\n/**\n * happy-dom's `textContent` setter treats a numeric `0` as empty (and `innerText`\n * throws on any non-string), whereas browsers stringify: `el.textContent = 0`\n * renders \"0\". A `textContent: count` binding assigns the raw number, so a count\n * that reaches zero would vanish only under happy-dom. Wrap the setters on every\n * prototype that owns one (Node, Element, HTMLElement) to coerce the way the\n * DOM spec does (`null` → \"\", anything else → `String(value)`). Idempotent per\n * prototype, so a fresh window from `installDom()` gets patched too.\n */\nfunction patchTextSetters(): void {\n const targets: Array<[unknown, string]> = [\n [globalThis.Node, \"textContent\"],\n [globalThis.Element, \"textContent\"],\n [globalThis.HTMLElement, \"textContent\"],\n [globalThis.HTMLElement, \"innerText\"],\n ];\n for (const [ctor, property] of targets) {\n const proto = (ctor as { prototype?: object } | undefined)?.prototype as (Record<symbol, unknown> & object) | undefined;\n if (proto === undefined) continue;\n const marker = `${String(TEXT_SETTER_PATCHED)}:${property}`;\n // own-property check: Element.prototype inherits Node.prototype's marker, and an\n // inherited marker must not make the Element setter (the one happy-dom uses) skip.\n if (Object.prototype.hasOwnProperty.call(proto, marker)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(proto, property);\n if (descriptor?.set === undefined) continue;\n const originalSet = descriptor.set;\n Object.defineProperty(proto, property, {\n ...descriptor,\n set(this: unknown, value: unknown) {\n originalSet.call(this, value === null || value === undefined ? \"\" : String(value));\n },\n });\n Object.defineProperty(proto, marker, { value: true, enumerable: false, configurable: true });\n }\n}\n\n/**\n * The global key a renderer installs its module resolver under, for `<wcs-state src>` (the\n * ssr-snapshot protocol's `SSR_MODULE_RESOLVER_KEY`, /protocol/ssr-snapshot.ts): `Symbol.for`, so\n * `@wcstack/state` reads the same key without either package importing the other's copy.\n */\nconst MODULE_RESOLVER_KEY = Symbol.for(\"wcstack.ssr.resolveModule\");\n\n/** The number of the mount (each mount's `src` modules are its own). */\nlet mounts = 0;\n\n/**\n * Puts a resolver for `<wcs-state src>` modules on the global key for the length of one mount, as\n * `renderToString` does for a render: `file:` / `data:` are imported as they are, any other URL from\n * the file `resolve` returns — with the mount's number in its query, since Node keeps a module per URL\n * (without it, every mount would get the first mount's state object). Without `resolve`, or when it\n * returns nothing, it throws saying what to do (Node's own error was `ERR_UNSUPPORTED_ESM_URL_SCHEME`).\n * Returns the function that puts back what was there.\n */\nfunction installModuleResolver(resolve: MountOptions[\"resolveModule\"]): () => void {\n const g = globalThis as Record<symbol, unknown>;\n const had = Object.prototype.hasOwnProperty.call(g, MODULE_RESOLVER_KEY);\n const previous = g[MODULE_RESOLVER_KEY];\n const n = ++mounts;\n g[MODULE_RESOLVER_KEY] = (url: string): string | null => {\n const u = new URL(url);\n if (u.protocol === \"file:\" || u.protocol === \"data:\") return null;\n const local = resolve?.(u);\n if (local === null || local === undefined) {\n throw new Error(\n `@wcstack/testing: <wcs-state src> names ${url}, which Node cannot import (its loader reads file: and data: URLs). ` +\n (resolve === undefined\n ? `Pass mount(html, { resolveModule }) to map it to a local file — e.g. resolveModule: (url) => new URL(\".\" + url.pathname, import.meta.url) — or write the state in the HTML (json=, or an inline <script type=\"module\">).`\n : \"resolveModule returned no file for it.\"),\n );\n }\n const file = local instanceof URL ? new URL(local.href) : /^file:/i.test(local) ? new URL(local) : pathToFileURL(local);\n file.searchParams.set(\"wcs-mount\", String(n));\n return file.href;\n };\n return () => {\n if (had) g[MODULE_RESOLVER_KEY] = previous;\n else delete g[MODULE_RESOLVER_KEY];\n };\n}\n\n/**\n * A `<wcs-state>` in `html` that would wait for `setInitialState()`: none of `state` / `src` / `json`\n * and no inline `<script type=\"module\">` (the element's resolution order, @wcstack/state README \"State\n * Initialization\"). A test cannot call `setInitialState()` before `mount()` returns, so `mount()` would\n * never resolve — it used to time out without a word. A root or a volume (`mount=`); not a\n * `bind-component` element (its state is its host's property), and not one inside a `<template>`\n * (template content is not in the page: a `for:` / `if:` row, a route, a DCC definition).\n */\nfunction findWaitingState(html: string, stateTagName: string): Element | undefined {\n const probe = document.createElement(\"template\");\n probe.innerHTML = html;\n return [...probe.content.querySelectorAll(stateTagName)].find((el) =>\n !el.hasAttribute(\"bind-component\") &&\n !el.hasAttribute(\"state\") &&\n !el.hasAttribute(\"src\") &&\n !el.hasAttribute(\"json\") &&\n el.querySelector('script[type=\"module\"]') === null);\n}\n\n/** `<wcs-state mount=\"cfg\">` — the element as written (its start tag), for a message. */\nfunction startTag(el: Element): string {\n const attributes = [...el.attributes].map((a) => (a.value === \"\" ? ` ${a.name}` : ` ${a.name}=\"${a.value}\"`));\n return `<${el.localName}${attributes.join(\"\")}>`;\n}\n\nasync function defaultBootstraps(): Promise<readonly BootstrapFunction[]> {\n // Lazy: the element classes bind their base class at module evaluation, which\n // must happen after the DOM globals exist (installDom() in bare Node).\n const { bootstrapState } = await import(\"@wcstack/state\");\n return [bootstrapState];\n}\n\nconst MOUNT_OPTIONS: OptionSpec = {\n root: [(value) => value === \"document\" || value === \"shadow\", `\"document\" or \"shadow\"`],\n bootstrap: [(value) => Array.isArray(value) && value.every(isFunction), \"an array of functions\"],\n stateTagName: [isString, \"a string\"],\n maxIterations: [isNumber, \"a number\"],\n resolveModule: [isFunction, \"a function\"],\n};\n\nexport async function mount(html: string, options: MountOptions = {}): Promise<MountedApp> {\n // before anything touches the document: a misspelt option used to be ignored\n checkOptions(\"mount()\", options, MOUNT_OPTIONS);\n const stateTagName = options.stateTagName ?? \"wcs-state\";\n const waiting = findWaitingState(html, stateTagName);\n if (waiting !== undefined) {\n throw new Error(\n `@wcstack/testing: ${startTag(waiting)} has no state to load (no state, src or json attribute, and no inline <script type=\"module\">), ` +\n `so it waits for setInitialState() — which a test cannot call before mount() returns: mount() would never resolve. ` +\n `Give it its state in the HTML: json='{\"count\": 0}', an inline <script type=\"module\">export default { … }</script>, ` +\n `or src=\"./state.js\" with mount(html, { resolveModule }).`,\n );\n }\n\n patchInlineScriptLoader();\n patchTextSetters();\n\n for (const bootstrap of options.bootstrap ?? (await defaultBootstraps())) {\n await bootstrap();\n }\n\n let root: Document | ShadowRoot;\n let container: Element;\n const restoreResolver = installModuleResolver(options.resolveModule);\n try {\n if (options.root === \"shadow\") {\n const host = document.createElement(\"div\");\n host.setAttribute(\"data-wcs-testing-host\", \"\");\n const shadow = host.attachShadow({ mode: \"open\" });\n shadow.innerHTML = html;\n document.body.appendChild(host);\n root = shadow;\n container = host;\n } else {\n document.body.innerHTML = html;\n root = document;\n container = document.body;\n }\n\n await waitForReady(root, { maxIterations: options.maxIterations });\n } finally {\n restoreResolver();\n }\n\n return {\n root,\n container,\n state(...legacyArgs: never[]): StateHandle {\n if (legacyArgs.length > 0) {\n throw new Error(\n `@wcstack/testing: state(name) was removed in v2 — there is one state tree per root. ` +\n `Mount the named state onto the tree (<wcs-state mount=\"...\">) and read it by its path prefix via state().`,\n );\n }\n const element = [...root.querySelectorAll<StateElementLike>(stateTagName)]\n .find((el) => !el.hasAttribute(\"mount\") && !el.hasAttribute(\"bind-component\"));\n if (element === undefined) {\n throw new Error(`@wcstack/testing: no <${stateTagName}> under the mounted root`);\n }\n return {\n element,\n read(fn) {\n let out!: ReturnType<typeof fn>;\n element.createState(\"readonly\", (state) => {\n out = fn(state);\n });\n return out;\n },\n async write(fn) {\n await element.createStateAsync(\"writable\", async (state) => {\n await fn(state);\n });\n },\n };\n },\n unmount() {\n if (root === document) {\n document.body.innerHTML = \"\";\n } else {\n container.remove();\n }\n },\n };\n}\n","/**\n * Let a state write reach the DOM.\n *\n * `@wcstack/state` applies updates on the microtask queue; two microtask turns\n * cover the write → apply chain, and one macrotask (`setTimeout(0)`) covers\n * anything a binding defers (e.g. a `customElements.whenDefined` re-apply).\n * This is exactly the wait the README recipe uses, fixed in one place.\n */\nexport async function settle(): Promise<void> {\n await Promise.resolve();\n await Promise.resolve();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n}\n","import { checkOptions, isAny, isBoolean, type OptionSpec } from \"./options.js\";\n\nconst FIRE_INIT: OptionSpec = {\n bubbles: [isBoolean, \"a boolean\"],\n cancelable: [isBoolean, \"a boolean\"],\n composed: [isBoolean, \"a boolean\"],\n detail: [isAny, \"any value\"],\n};\n\n/**\n * Dispatch a DOM event the way a user action would: bubbling by default, a\n * `CustomEvent` when `detail` is given, a plain `Event` otherwise.\n *\n * Returns what `dispatchEvent` returns (`false` when a handler called\n * `preventDefault()`). Follow with `await settle()` before asserting the DOM.\n * An `init` key other than `bubbles` / `cancelable` / `composed` / `detail` throws:\n * it would not reach the event (`{ key: \"Enter\" }` makes no `KeyboardEvent`).\n */\nexport function fire(target: EventTarget, type: string, init: EventInit & { detail?: unknown } = {}): boolean {\n checkOptions(\n \"fire()\",\n init,\n FIRE_INIT,\n \" fire() dispatches an Event, or a CustomEvent with detail; for another event class (KeyboardEvent, InputEvent, …) call target.dispatchEvent() yourself.\",\n );\n const { detail, ...eventInit } = init;\n const event = detail !== undefined\n ? new CustomEvent(type, { bubbles: true, ...eventInit, detail })\n : new Event(type, { bubbles: true, ...eventInit });\n return target.dispatchEvent(event);\n}\n","/**\n * Bare Node (no vitest `environment: 'happy-dom'`): create a happy-dom `Window`\n * and install its globals, using the same `installGlobals` `@wcstack/server` runs\n * for SSR. Returns an async restore function that also closes the window.\n *\n * Import order matters: `@wcstack/state`'s element classes pick their base class\n * when the module is evaluated, so `mount()` imports it lazily — after this\n * function has installed `HTMLElement`. Do the same for any other wcstack\n * package you bootstrap (`async () => (await import(\"@wcstack/router\")).bootstrapRouter()`).\n *\n * `happy-dom` is an optional peer: it is only needed here. A `window` can also be\n * passed in (`installDom({ window })`) to skip the import entirely.\n */\n\nimport { installGlobals } from \"@wcstack/server\";\nimport { checkOptions, isObject, isString, type OptionSpec } from \"./options.js\";\n\nconst INSTALL_DOM_OPTIONS: OptionSpec = {\n url: [isString, \"a string\"],\n window: [isObject, \"a happy-dom Window\"],\n};\n\nexport interface InstallDomOptions {\n /** `window.location` for the page (default `http://localhost/`). */\n readonly url?: string;\n /** A ready-made happy-dom `Window`; when given, `happy-dom` is not imported. */\n readonly window?: unknown;\n}\n\ninterface HappyDomWindowLike {\n readonly happyDOM: { close(): Promise<void> };\n}\n\nexport async function installDom(options: InstallDomOptions = {}): Promise<() => Promise<void>> {\n checkOptions(\"installDom()\", options, INSTALL_DOM_OPTIONS);\n const window = options.window ?? (await createWindow(options.url ?? \"http://localhost/\"));\n const restore = installGlobals(window as Parameters<typeof installGlobals>[0]);\n return async () => {\n restore();\n await (window as HappyDomWindowLike).happyDOM.close();\n };\n}\n\nasync function createWindow(url: string): Promise<unknown> {\n let mod: { Window: new (options: { url: string }) => unknown };\n try {\n mod = (await import(\"happy-dom\")) as typeof mod;\n } catch {\n throw new Error(\n \"@wcstack/testing: installDom() needs happy-dom (npm i -D happy-dom), or pass { window } — under vitest with environment: 'happy-dom' it is not needed at all\",\n );\n }\n return new mod.Window({ url });\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/testing",
3
- "version": "4.10.1",
3
+ "version": "4.11.2",
4
4
  "description": "Headless test helpers for wcstack pages: mount HTML under happy-dom, await bindings, read/write state, settle updates - the README recipe as one import.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "license": "MIT",
45
45
  "peerDependencies": {
46
- "@wcstack/server": "^4.10.1",
47
- "@wcstack/state": "^4.10.1",
46
+ "@wcstack/server": "^4.11.2",
47
+ "@wcstack/state": "^4.11.2",
48
48
  "happy-dom": ">=20"
49
49
  },
50
50
  "peerDependenciesMeta": {
@@ -64,7 +64,7 @@
64
64
  "@wcstack/state": "file:../state",
65
65
  "eslint": "^9.39.1",
66
66
  "globals": "^16.2.0",
67
- "happy-dom": "^20.0.11",
67
+ "happy-dom": "^21.0.4",
68
68
  "rimraf": "^6.0.1",
69
69
  "rollup": "^4.22.4",
70
70
  "rollup-plugin-dts": "^6.1.1",