@vielzeug/assay 1.0.0 → 2.0.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/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  **Package:** `@vielzeug/assay`  ·  **Category:** Testing
11
11
 
12
- **Key exports:** `within`, `queryByText`, `queryAllByText`, `fire`, `createPointerEvent`, `waitFor`, `waitForEvent`, `AssayError`, `AssayTimeoutError`
12
+ **Key exports:** `within`, `fireClick`, `fireInput`, `fireKeyDown`, `waitUntil`, `retry`, `waitForEvent`, `AssayError`, `AssayQueryError`, `AssayTimeoutError`
13
13
 
14
14
  **When to use:** Writing DOM-level tests for any custom-element or vanilla-DOM code — scoped element queries, low-level synchronous event dispatch, and deterministic async waiting — without pulling in a full testing-library dependency or coupling to a specific component framework.
15
15
 
@@ -30,14 +30,14 @@ yarn add -D @vielzeug/assay
30
30
  ## Quick Start
31
31
 
32
32
  ```ts
33
- import { fire, waitFor, within } from '@vielzeug/assay';
33
+ import { fireClick, waitUntil, within } from '@vielzeug/assay';
34
34
 
35
35
  const panel = document.querySelector('.panel')!;
36
- const { query, queryByText } = within(panel);
36
+ const view = within(panel);
37
37
 
38
- fire.click(query('button.submit')!);
38
+ fireClick(view.get('button.submit'));
39
39
 
40
- await waitFor(() => queryByText('Saved') !== null);
40
+ await waitUntil(() => view.queryByText('Saved') !== null);
41
41
  ```
42
42
 
43
43
  ## Documentation
package/dist/assay.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=!globalThis.__ASSAY_PROD__,r=new Set;function i(e,t){!n||r.has(e)||(r.add(e),console.warn(`[@vielzeug/assay] ${t}`))}var a=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):(i(`pointer-fallback`,`PointerEvent is unavailable in this environment — falling back to MouseEvent. pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.`),new MouseEvent(e,t)),o={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom:(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),event:(e,t)=>e.dispatchEvent(t),focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerCancel:(e,t)=>e.dispatchEvent(a(`pointercancel`,{bubbles:!0,cancelable:!1,...t})),pointerDown:(e,t)=>e.dispatchEvent(a(`pointerdown`,{bubbles:!0,cancelable:!0,...t})),pointerEnter:(e,t)=>e.dispatchEvent(a(`pointerenter`,{bubbles:!1,...t})),pointerLeave:(e,t)=>e.dispatchEvent(a(`pointerleave`,{bubbles:!1,...t})),pointerMove:(e,t)=>e.dispatchEvent(a(`pointermove`,{bubbles:!0,cancelable:!0,...t})),pointerUp:(e,t)=>e.dispatchEvent(a(`pointerup`,{bubbles:!0,cancelable:!0,...t})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch:(e,t,n)=>typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):(i(`touch-fallback`,"TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data."),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n})))};function s(e,t){return e.querySelector(t)}function c(e,t){return Array.from(e.querySelectorAll(t))}function l(e,t){return e.querySelector(`[data-testid="${t}"]`)}function u(e,t){return Array.from(e.querySelectorAll(`[data-testid="${t}"]`))}function d(e,t,n){for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null}function f(e,t,n){return Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t)}function p(e,t){return e.shadowRoot?.querySelector(t)??null}function m(e,t){return Array.from(e.shadowRoot?.querySelectorAll(t)??[])}function h(e,t){return p(e,`[part="${t}"]`)}function g(e,t){let n=t?`[slot="${t}"]`:`:not([slot])`;return Array.from(e.querySelectorAll(`:scope > ${n}`))}function _(e){return{query:t=>s(e,t),queryAll:t=>c(e,t),queryAllByTestId:t=>u(e,t),queryAllByText:(t,n=`*`)=>f(e,t,n),queryByTestId:t=>l(e,t),queryByText:(t,n=`*`)=>d(e,t,n)}}async function v(e,{interval:n=50,message:r,timeout:i=1e3}={}){let a=Date.now()+i,o;for(;;){try{let t=await e();if(t===void 0||t)return}catch(e){o=e}if(Date.now()>=a)break;await x(n)}let s=`waitFor timed out after ${i}ms`,c=r?`${r} (${s})`:s,l=o instanceof Error?o.message:o==null?void 0:String(o);throw new t(l?`${c}\n${l}`:c,{cause:o})}function y(e,n,r=1e3){return new Promise((i,a)=>{let o=e=>{clearTimeout(s),i(e)},s=setTimeout(()=>{e.removeEventListener(n,o),a(new t(`waitForEvent: "${n}" timed out after ${r}ms`))},r);e.addEventListener(n,o,{once:!0})})}var b=()=>new Promise(e=>{queueMicrotask(e)}),x=(e=0)=>new Promise(t=>{setTimeout(t,e)});exports.AssayError=e,exports.AssayTimeoutError=t,exports.createPointerEvent=a,exports.fire=o,exports.getSlotted=g,exports.nextTick=b,exports.query=s,exports.queryAll=c,exports.queryAllByTestId=u,exports.queryAllByText=f,exports.queryAllInShadow=m,exports.queryByTestId=l,exports.queryByText=d,exports.queryInShadow=p,exports.queryPart=h,exports.wait=x,exports.waitFor=v,exports.waitForEvent=y,exports.within=_;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{},r=(e,t)=>e.dispatchEvent(t),i=(e,t={})=>r(e,new FocusEvent(`blur`,t)),a=(e,t={})=>r(e,new Event(`change`,{bubbles:!0,...t})),o=(e,t={})=>r(e,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),s=(e,{type:t,...n})=>r(e,new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),c=(e,t={})=>r(e,new FocusEvent(`focus`,t)),l=(e,t={})=>r(e,new InputEvent(`input`,{bubbles:!0,...t})),u=(e,t={})=>r(e,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),d=(e,t={})=>r(e,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),f=(e,t={})=>r(e,new SubmitEvent(`submit`,{bubbles:!0,cancelable:!0,...t})),p=e=>e instanceof Element?e.outerHTML.slice(0,500):e instanceof ShadowRoot?`<shadow-root>${e.innerHTML.slice(0,500)}</shadow-root>`:e.nodeName,m=(e,n,r)=>{if(e)return e;throw new t(`Unable to find ${n} within ${p(r)}.`)},h=(e,t)=>e.querySelector(t),g=(e,t)=>Array.from(e.querySelectorAll(t)),_=(e,t)=>g(e,`[data-testid]`).find(e=>e.getAttribute(`data-testid`)===t)??null,v=(e,t)=>g(e,`[data-testid]`).filter(e=>e.getAttribute(`data-testid`)===t),y=(e,t,n=`*`)=>{for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null},b=(e,t,n=`*`)=>Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t);function x(e,t){return e.shadowRoot?h(e.shadowRoot,t):null}function S(e,t){return e.shadowRoot?g(e.shadowRoot,t):[]}function C(e,t){return S(e,`[part]`).find(e=>e.getAttribute(`part`)?.split(/\s+/).includes(t))??null}function w(e,t){return Array.from(e.children).filter(e=>t===void 0?!e.getAttribute(`slot`):e.getAttribute(`slot`)===t)}function T(e){return{get:t=>m(h(e,t),`"${t}"`,e),getByTestId:t=>m(_(e,t),`data-testid "${t}"`,e),getByText:(t,n=`*`)=>m(y(e,t,n),`text "${t}" matching "${n}"`,e),query:t=>h(e,t),queryAll:t=>g(e,t),queryAllByTestId:t=>v(e,t),queryAllByText:(t,n=`*`)=>b(e,t,n),queryByTestId:t=>_(e,t),queryByText:(t,n=`*`)=>y(e,t,n)}}var E=e=>e.reason??new DOMException(`The operation was aborted.`,`AbortError`),D=(e=0,{signal:t}={})=>new Promise((n,r)=>{if(t?.aborted){r(E(t));return}let i=setTimeout(a,e);function a(){t?.removeEventListener(`abort`,o),n()}function o(){clearTimeout(i),r(E(t))}t?.addEventListener(`abort`,o,{once:!0})});async function O(e,{interval:t=50,signal:r,timeout:i=1e3}={}){let a=Date.now()+i;for(;;){if(r?.aborted)throw E(r);if(await e())return;if(Date.now()>=a)break;await D(t,{signal:r})}throw new n(`waitUntil timed out after ${i}ms`)}async function k(e,{interval:t=50,message:r,signal:i,timeout:a=1e3}={}){let o=Date.now()+a,s;for(;;){if(i?.aborted)throw E(i);try{await e();return}catch(e){s=e}if(Date.now()>=o)break;await D(t,{signal:i})}let c=`retry timed out after ${a}ms`,l=r?`${r} (${c})`:c,u=s instanceof Error?s.message:s==null?``:String(s);throw new n(u?`${l}\n${u}`:l,{cause:s})}function A(e,t,{signal:r,timeout:i=1e3}={}){return new Promise((a,o)=>{if(r?.aborted){o(E(r));return}let s=()=>{clearTimeout(l),r?.removeEventListener(`abort`,u),e.removeEventListener(t,c)},c=e=>{s(),a(e)},l=setTimeout(()=>{s(),o(new n(`waitForEvent: "${t}" timed out after ${i}ms`))},i),u=()=>{s(),o(E(r))};e.addEventListener(t,c,{once:!0}),r?.addEventListener(`abort`,u,{once:!0})})}var j=()=>new Promise(e=>{queueMicrotask(e)});exports.AssayError=e,exports.AssayQueryError=t,exports.AssayTimeoutError=n,exports.delay=D,exports.dispatch=r,exports.fireBlur=i,exports.fireChange=a,exports.fireClick=o,exports.fireCustom=s,exports.fireFocus=c,exports.fireInput=l,exports.fireKeyDown=u,exports.fireKeyUp=d,exports.fireSubmit=f,exports.getSlotted=w,exports.nextTick=j,exports.queryAllInShadow=S,exports.queryInShadow=x,exports.queryPart=C,exports.retry=k,exports.waitForEvent=A,exports.waitUntil=O,exports.within=T;
2
2
  //# sourceMappingURL=assay.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"assay.cjs","names":[],"sources":["../src/errors.ts","../src/_dev.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */\nexport class AssayTimeoutError extends AssayError {}\n","const isDev = !(globalThis as { __ASSAY_PROD__?: boolean }).__ASSAY_PROD__;\n\nconst warnedKeys = new Set<string>();\n\n/**\n * @internal Warn at most once per `key` for the lifetime of the module — used for\n * environment-capability fallbacks (e.g. missing `PointerEvent`/`TouchEvent`), which would\n * otherwise log once per dispatched event in a suite that fires hundreds of them.\n */\nexport function warnOnce(key: string, msg: string): void {\n if (!isDev || warnedKeys.has(key)) return;\n\n warnedKeys.add(key);\n console.warn(`[@vielzeug/assay] ${msg}`);\n}\n\n/** @internal Test-only — clears \"warned once\" state so fallback-warning tests can run in any order. */\nexport function _resetWarnings(): void {\n warnedKeys.clear();\n}\n","/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\nimport { warnOnce } from './_dev';\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n warnOnce(\n 'pointer-fallback',\n 'PointerEvent is unavailable in this environment — falling back to MouseEvent. ' +\n 'pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.',\n );\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously. Every method dispatches an appropriately-typed\n * `Event` subclass with sensible defaults and returns `dispatchEvent`'s own boolean result\n * (`false` when a listener called `preventDefault()`) — the same contract for all of them,\n * with no exceptions.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom: (el: Element, name: string, opts?: CustomEventInit) =>\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts })),\n /** Dispatch any pre-built Event instance directly. */\n event: (el: Element, event: Event) => el.dispatchEvent(event),\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerCancel: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointercancel', { bubbles: true, cancelable: false, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerMove: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointermove', { bubbles: true, cancelable: true, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch: (el: Element, type: string, opts?: EventInit) => {\n if (typeof TouchEvent !== 'undefined') {\n return el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n\n warnOnce(\n 'touch-fallback',\n 'TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data.',\n );\n\n return el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n },\n} as const;\n","/**\n * DOM query helpers for test environments.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any DOM element — see {@link within} */\nexport interface QueryScope {\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Query a single element within `root`. Equivalent to `root.querySelector(selector)`. */\nexport function query<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E | null {\n return root.querySelector<E>(selector);\n}\n\n/** Query every element within `root` matching `selector`. */\nexport function queryAll<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E[] {\n return Array.from(root.querySelectorAll<E>(selector));\n}\n\n/** Query a single element within `root` by its `data-testid` attribute. */\nexport function queryByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E | null {\n return root.querySelector<E>(`[data-testid=\"${testId}\"]`);\n}\n\n/** Query every element within `root` matching a `data-testid` attribute. */\nexport function queryAllByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E[] {\n return Array.from(root.querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n}\n\nexport function queryByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E | null {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n}\n\nexport function queryAllByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E[] {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n}\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot?.querySelector<E>(selector) ?? null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return Array.from(host.shadowRoot?.querySelectorAll<E>(selector) ?? []);\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return queryInShadow<E>(host, `[part=\"${part}\"]`);\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every slotted child (elements\n * with no `slot` attribute) when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll<E>(`:scope > ${selector}`));\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to any element — useful for slotted/light DOM content.\n *\n * @example\n * const panel = fixture.query('.panel')!;\n * const { query } = within(panel);\n * expect(query('.title')?.textContent).toBe('Hello');\n */\nexport function within(element: Element): QueryScope {\n return {\n query: <E extends Element = Element>(selector: string) => query<E>(element, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(element, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(element, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(element, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(element, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(element, text, selector),\n };\n}\n","/**\n * Async waiting utilities for test environments.\n */\n\nimport { AssayTimeoutError } from './errors';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface WaitOptions {\n /** Maximum wait time in ms (default: 1000) */\n timeout?: number;\n /** Polling interval in ms (default: 50) */\n interval?: number;\n /** Message included in timeout error */\n message?: string;\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Poll until a callback returns truthy (or void) without throwing.\n * Supports both boolean conditions and `expect()` assertions.\n *\n * - Returns truthy → success\n * - Returns `undefined` (e.g. bare `expect()` call) → success\n * - Returns falsy value → retry\n * - Throws → retry\n *\n * Always rejects with `AssayTimeoutError` on timeout, regardless of which of the two retry\n * reasons above was last observed — `instanceof AssayTimeoutError` is a reliable way to catch\n * \"the condition never became true\" without caring whether the last attempt returned falsy or\n * threw. The original failure (an assertion's message, a thrown error) is preserved as `.cause`,\n * not merged into a mutated copy of it — `fn`'s thrown errors are caller-owned and never mutated.\n *\n * @example\n * await waitFor(() => query('.status')?.textContent === 'loaded');\n * await waitFor(() => expect(count).toBe(3));\n */\nexport async function waitFor(\n fn: () => unknown,\n { interval = 50, message, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n // Single loop, one `fn()` call site — always tries once more before checking the deadline,\n // so a condition that becomes true exactly as the timeout elapses still succeeds.\n for (;;) {\n try {\n const result = await fn();\n\n if (result === undefined || result) return;\n } catch (e) {\n lastError = e;\n }\n\n if (Date.now() >= deadline) break;\n\n await wait(interval);\n }\n\n // The default timing summary always appears — a custom `message` adds context in front of it\n // rather than replacing it, so a debugging session never loses \"how long did this actually wait\".\n const timing = `waitFor timed out after ${timeout}ms`;\n const base = message ? `${message} (${timing})` : timing;\n const cause = lastError instanceof Error ? lastError.message : lastError != null ? String(lastError) : undefined;\n\n throw new AssayTimeoutError(cause ? `${base}\\n${cause}` : base, { cause: lastError });\n}\n\n/**\n * Resolve when the target element emits the given event.\n *\n * @example\n * const promise = waitForEvent(el, 'change');\n * fire.click(trigger);\n * const event = await promise;\n */\nexport function waitForEvent<T extends Event = Event>(element: Element, name: string, timeout = 1000): Promise<T> {\n return new Promise((resolve, reject) => {\n const onEvent = (e: Event): void => {\n clearTimeout(timer);\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n element.removeEventListener(name, onEvent);\n reject(new AssayTimeoutError(`waitForEvent: \"${name}\" timed out after ${timeout}ms`));\n }, timeout);\n\n element.addEventListener(name, onEvent, { once: true });\n });\n}\n\n/**\n * Resolves after one microtask tick (`queueMicrotask`). Use to wait for reactivity (signal\n * effects, promise-chain continuations) to settle without moving into the macrotask queue —\n * cheaper and more precise than `wait(0)`, which yields to a full `setTimeout` turn instead.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Resolves after `ms` milliseconds. Prefer `nextTick()` for reactive updates and `waitFor()`\n * for polling a condition — reach for this only when a real macrotask delay is what the code\n * under test actually depends on (e.g. a debounce timer).\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"assay.cjs","names":[],"sources":["../src/errors.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown when a required query has no match. */\nexport class AssayQueryError extends AssayError {}\n\n/** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */\nexport class AssayTimeoutError extends AssayError {}\n","/** Synchronous, exact DOM event dispatchers for test environments. */\n\n/** Dispatch an event instance without modifying it. */\nexport const dispatch = (target: EventTarget, event: Event): boolean => target.dispatchEvent(event);\n\nexport const fireBlur = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('blur', init));\n\nexport const fireChange = (target: EventTarget, init: EventInit = {}): boolean =>\n dispatch(target, new Event('change', { bubbles: true, ...init }));\n\nexport const fireClick = (target: EventTarget, init: MouseEventInit = {}): boolean =>\n dispatch(target, new MouseEvent('click', { bubbles: true, cancelable: true, ...init }));\n\nexport interface CustomEventOptions<T> extends CustomEventInit<T> {\n type: string;\n}\n\nexport const fireCustom = <T>(target: EventTarget, { type, ...init }: CustomEventOptions<T>): boolean =>\n dispatch(target, new CustomEvent(type, { bubbles: true, cancelable: true, composed: false, ...init }));\n\nexport const fireFocus = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('focus', init));\n\nexport const fireInput = (target: EventTarget, init: InputEventInit = {}): boolean =>\n dispatch(target, new InputEvent('input', { bubbles: true, ...init }));\n\nexport const fireKeyDown = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireKeyUp = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireSubmit = (target: EventTarget, init: SubmitEventInit = {}): boolean =>\n dispatch(target, new SubmitEvent('submit', { bubbles: true, cancelable: true, ...init }));\n","import { AssayQueryError } from './errors';\n\n/** DOM query helpers for test environments. */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any queryable DOM root — see {@link within}. */\nexport interface QueryScope {\n get<E extends Element = Element>(selector: string): E;\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n getByText<E extends Element = Element>(text: string, selector?: string): E;\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n getByTestId<E extends Element = Element>(testId: string): E;\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\ntype QueryRoot = ParentNode;\n\nconst describeRoot = (root: QueryRoot): string => {\n if (root instanceof Element) return root.outerHTML.slice(0, 500);\n\n if (root instanceof ShadowRoot) return `<shadow-root>${root.innerHTML.slice(0, 500)}</shadow-root>`;\n\n return root.nodeName;\n};\n\nconst requireMatch = <E extends Element>(element: E | null, description: string, root: QueryRoot): E => {\n if (element) return element;\n\n throw new AssayQueryError(`Unable to find ${description} within ${describeRoot(root)}.`);\n};\n\nconst query = <E extends Element = Element>(root: QueryRoot, selector: string): E | null =>\n root.querySelector<E>(selector);\n\nconst queryAll = <E extends Element = Element>(root: QueryRoot, selector: string): E[] => {\n return Array.from(root.querySelectorAll<E>(selector));\n};\n\nconst queryByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E | null =>\n queryAll<E>(root, '[data-testid]').find((element) => element.getAttribute('data-testid') === testId) ?? null;\n\nconst queryAllByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E[] =>\n queryAll<E>(root, '[data-testid]').filter((element) => element.getAttribute('data-testid') === testId);\n\nconst queryByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E | null => {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n};\n\nconst queryAllByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E[] => {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n};\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot ? query<E>(host.shadowRoot, selector) : null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return host.shadowRoot ? queryAll<E>(host.shadowRoot, selector) : [];\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return (\n queryAllInShadow<E>(host, '[part]').find((element) => element.getAttribute('part')?.split(/\\s+/).includes(part)) ??\n null\n );\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every child assigned to the\n * default slot when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n return Array.from(host.children).filter((element) =>\n slotName === undefined ? !element.getAttribute('slot') : element.getAttribute('slot') === slotName,\n ) as E[];\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to a DOM root — useful for light DOM and shadow roots.\n *\n * @example\n * const view = within(fixture.shadow!);\n * expect(view.get('.title').textContent).toBe('Hello');\n */\nexport function within(root: QueryRoot): QueryScope {\n return {\n get: <E extends Element = Element>(selector: string) =>\n requireMatch(query<E>(root, selector), `\"${selector}\"`, root),\n getByTestId: <E extends Element = Element>(testId: string) =>\n requireMatch(queryByTestId<E>(root, testId), `data-testid \"${testId}\"`, root),\n getByText: <E extends Element = Element>(text: string, selector = '*') =>\n requireMatch(queryByText<E>(root, text, selector), `text \"${text}\" matching \"${selector}\"`, root),\n query: <E extends Element = Element>(selector: string) => query<E>(root, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(root, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(root, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(root, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(root, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(root, text, selector),\n };\n}\n","import { AssayTimeoutError } from './errors';\n\nexport interface WaitOptions {\n /** Cancel the pending wait. */\n signal?: AbortSignal;\n /** Maximum wait time in ms (default: 1000). */\n timeout?: number;\n /** Polling interval in ms (default: 50). */\n interval?: number;\n}\n\nexport interface RetryOptions extends WaitOptions {\n /** Context included in the timeout error. */\n message?: string;\n}\n\nexport interface DelayOptions {\n /** Cancel the pending delay. */\n signal?: AbortSignal;\n}\n\nconst abortReason = (signal: AbortSignal): unknown =>\n signal.reason ?? new DOMException('The operation was aborted.', 'AbortError');\n\n/**\n * Resolve after `ms` milliseconds. Rejects with the abort reason when cancelled.\n */\nexport const delay = (ms = 0, { signal }: DelayOptions = {}): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const timer = setTimeout(done, ms);\n\n function done(): void {\n signal?.removeEventListener('abort', abort);\n resolve();\n }\n\n function abort(): void {\n clearTimeout(timer);\n reject(abortReason(signal!));\n }\n\n signal?.addEventListener('abort', abort, { once: true });\n });\n\n/** Poll until a synchronous or asynchronous predicate returns `true`. */\nexport async function waitUntil(\n predicate: () => boolean | Promise<boolean>,\n { interval = 50, signal, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n if (await predicate()) return;\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n throw new AssayTimeoutError(`waitUntil timed out after ${timeout}ms`);\n}\n\n/** Retry an assertion until it stops throwing. */\nexport async function retry(\n assertion: () => void | Promise<void>,\n { interval = 50, message, signal, timeout = 1000 }: RetryOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n try {\n await assertion();\n\n return;\n } catch (error) {\n lastError = error;\n }\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n const timing = `retry timed out after ${timeout}ms`;\n const context = message ? `${message} (${timing})` : timing;\n const detail = lastError instanceof Error ? lastError.message : lastError == null ? '' : String(lastError);\n\n throw new AssayTimeoutError(detail ? `${context}\\n${detail}` : context, { cause: lastError });\n}\n\n/** Resolve with the next event of `type`, or reject when it times out or is aborted. */\nexport function waitForEvent<T extends Event = Event>(\n target: EventTarget,\n type: string,\n { signal, timeout = 1000 }: Omit<WaitOptions, 'interval'> = {},\n): Promise<T> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const dispose = () => {\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n target.removeEventListener(type, onEvent);\n };\n const onEvent = (e: Event): void => {\n dispose();\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n dispose();\n reject(new AssayTimeoutError(`waitForEvent: \"${type}\" timed out after ${timeout}ms`));\n }, timeout);\n const onAbort = () => {\n dispose();\n reject(abortReason(signal!));\n };\n\n target.addEventListener(type, onEvent, { once: true });\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Resolve after one microtask tick. */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var Assay=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=!globalThis.__ASSAY_PROD__,i=new Set;function a(e,t){!r||i.has(e)||(i.add(e),console.warn(`[@vielzeug/assay] ${t}`))}var o=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):(a(`pointer-fallback`,`PointerEvent is unavailable in this environment — falling back to MouseEvent. pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.`),new MouseEvent(e,t)),s={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom:(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),event:(e,t)=>e.dispatchEvent(t),focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerCancel:(e,t)=>e.dispatchEvent(o(`pointercancel`,{bubbles:!0,cancelable:!1,...t})),pointerDown:(e,t)=>e.dispatchEvent(o(`pointerdown`,{bubbles:!0,cancelable:!0,...t})),pointerEnter:(e,t)=>e.dispatchEvent(o(`pointerenter`,{bubbles:!1,...t})),pointerLeave:(e,t)=>e.dispatchEvent(o(`pointerleave`,{bubbles:!1,...t})),pointerMove:(e,t)=>e.dispatchEvent(o(`pointermove`,{bubbles:!0,cancelable:!0,...t})),pointerUp:(e,t)=>e.dispatchEvent(o(`pointerup`,{bubbles:!0,cancelable:!0,...t})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch:(e,t,n)=>typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):(a(`touch-fallback`,"TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data."),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n})))};function c(e,t){return e.querySelector(t)}function l(e,t){return Array.from(e.querySelectorAll(t))}function u(e,t){return e.querySelector(`[data-testid="${t}"]`)}function d(e,t){return Array.from(e.querySelectorAll(`[data-testid="${t}"]`))}function f(e,t,n){for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null}function p(e,t,n){return Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t)}function m(e,t){return e.shadowRoot?.querySelector(t)??null}function h(e,t){return Array.from(e.shadowRoot?.querySelectorAll(t)??[])}function g(e,t){return m(e,`[part="${t}"]`)}function _(e,t){let n=t?`[slot="${t}"]`:`:not([slot])`;return Array.from(e.querySelectorAll(`:scope > ${n}`))}function v(e){return{query:t=>c(e,t),queryAll:t=>l(e,t),queryAllByTestId:t=>d(e,t),queryAllByText:(t,n=`*`)=>p(e,t,n),queryByTestId:t=>u(e,t),queryByText:(t,n=`*`)=>f(e,t,n)}}async function y(e,{interval:t=50,message:r,timeout:i=1e3}={}){let a=Date.now()+i,o;for(;;){try{let t=await e();if(t===void 0||t)return}catch(e){o=e}if(Date.now()>=a)break;await S(t)}let s=`waitFor timed out after ${i}ms`,c=r?`${r} (${s})`:s,l=o instanceof Error?o.message:o==null?void 0:String(o);throw new n(l?`${c}\n${l}`:c,{cause:o})}function b(e,t,r=1e3){return new Promise((i,a)=>{let o=e=>{clearTimeout(s),i(e)},s=setTimeout(()=>{e.removeEventListener(t,o),a(new n(`waitForEvent: "${t}" timed out after ${r}ms`))},r);e.addEventListener(t,o,{once:!0})})}var x=()=>new Promise(e=>{queueMicrotask(e)}),S=(e=0)=>new Promise(t=>{setTimeout(t,e)});return e.AssayError=t,e.AssayTimeoutError=n,e.createPointerEvent=o,e.fire=s,e.getSlotted=_,e.nextTick=x,e.query=c,e.queryAll=l,e.queryAllByTestId=d,e.queryAllByText=p,e.queryAllInShadow=h,e.queryByTestId=u,e.queryByText=f,e.queryInShadow=m,e.queryPart=g,e.wait=S,e.waitFor=y,e.waitForEvent=b,e.within=v,e})({});
1
+ var Assay=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=class extends t{},i=(e,t)=>e.dispatchEvent(t),a=(e,t={})=>i(e,new FocusEvent(`blur`,t)),o=(e,t={})=>i(e,new Event(`change`,{bubbles:!0,...t})),s=(e,t={})=>i(e,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),c=(e,{type:t,...n})=>i(e,new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),l=(e,t={})=>i(e,new FocusEvent(`focus`,t)),u=(e,t={})=>i(e,new InputEvent(`input`,{bubbles:!0,...t})),d=(e,t={})=>i(e,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),f=(e,t={})=>i(e,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),p=(e,t={})=>i(e,new SubmitEvent(`submit`,{bubbles:!0,cancelable:!0,...t})),m=e=>e instanceof Element?e.outerHTML.slice(0,500):e instanceof ShadowRoot?`<shadow-root>${e.innerHTML.slice(0,500)}</shadow-root>`:e.nodeName,h=(e,t,r)=>{if(e)return e;throw new n(`Unable to find ${t} within ${m(r)}.`)},g=(e,t)=>e.querySelector(t),_=(e,t)=>Array.from(e.querySelectorAll(t)),v=(e,t)=>_(e,`[data-testid]`).find(e=>e.getAttribute(`data-testid`)===t)??null,y=(e,t)=>_(e,`[data-testid]`).filter(e=>e.getAttribute(`data-testid`)===t),b=(e,t,n=`*`)=>{for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null},x=(e,t,n=`*`)=>Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t);function S(e,t){return e.shadowRoot?g(e.shadowRoot,t):null}function C(e,t){return e.shadowRoot?_(e.shadowRoot,t):[]}function w(e,t){return C(e,`[part]`).find(e=>e.getAttribute(`part`)?.split(/\s+/).includes(t))??null}function T(e,t){return Array.from(e.children).filter(e=>t===void 0?!e.getAttribute(`slot`):e.getAttribute(`slot`)===t)}function E(e){return{get:t=>h(g(e,t),`"${t}"`,e),getByTestId:t=>h(v(e,t),`data-testid "${t}"`,e),getByText:(t,n=`*`)=>h(b(e,t,n),`text "${t}" matching "${n}"`,e),query:t=>g(e,t),queryAll:t=>_(e,t),queryAllByTestId:t=>y(e,t),queryAllByText:(t,n=`*`)=>x(e,t,n),queryByTestId:t=>v(e,t),queryByText:(t,n=`*`)=>b(e,t,n)}}var D=e=>e.reason??new DOMException(`The operation was aborted.`,`AbortError`),O=(e=0,{signal:t}={})=>new Promise((n,r)=>{if(t?.aborted){r(D(t));return}let i=setTimeout(a,e);function a(){t?.removeEventListener(`abort`,o),n()}function o(){clearTimeout(i),r(D(t))}t?.addEventListener(`abort`,o,{once:!0})});async function k(e,{interval:t=50,signal:n,timeout:i=1e3}={}){let a=Date.now()+i;for(;;){if(n?.aborted)throw D(n);if(await e())return;if(Date.now()>=a)break;await O(t,{signal:n})}throw new r(`waitUntil timed out after ${i}ms`)}async function A(e,{interval:t=50,message:n,signal:i,timeout:a=1e3}={}){let o=Date.now()+a,s;for(;;){if(i?.aborted)throw D(i);try{await e();return}catch(e){s=e}if(Date.now()>=o)break;await O(t,{signal:i})}let c=`retry timed out after ${a}ms`,l=n?`${n} (${c})`:c,u=s instanceof Error?s.message:s==null?``:String(s);throw new r(u?`${l}\n${u}`:l,{cause:s})}function j(e,t,{signal:n,timeout:i=1e3}={}){return new Promise((a,o)=>{if(n?.aborted){o(D(n));return}let s=()=>{clearTimeout(l),n?.removeEventListener(`abort`,u),e.removeEventListener(t,c)},c=e=>{s(),a(e)},l=setTimeout(()=>{s(),o(new r(`waitForEvent: "${t}" timed out after ${i}ms`))},i),u=()=>{s(),o(D(n))};e.addEventListener(t,c,{once:!0}),n?.addEventListener(`abort`,u,{once:!0})})}return e.AssayError=t,e.AssayQueryError=n,e.AssayTimeoutError=r,e.delay=O,e.dispatch=i,e.fireBlur=a,e.fireChange=o,e.fireClick=s,e.fireCustom=c,e.fireFocus=l,e.fireInput=u,e.fireKeyDown=d,e.fireKeyUp=f,e.fireSubmit=p,e.getSlotted=T,e.nextTick=()=>new Promise(e=>{queueMicrotask(e)}),e.queryAllInShadow=C,e.queryInShadow=S,e.queryPart=w,e.retry=A,e.waitForEvent=j,e.waitUntil=k,e.within=E,e})({});
2
2
  //# sourceMappingURL=assay.iife.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assay.iife.js","names":[],"sources":["../src/errors.ts","../src/_dev.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */\nexport class AssayTimeoutError extends AssayError {}\n","const isDev = !(globalThis as { __ASSAY_PROD__?: boolean }).__ASSAY_PROD__;\n\nconst warnedKeys = new Set<string>();\n\n/**\n * @internal Warn at most once per `key` for the lifetime of the module — used for\n * environment-capability fallbacks (e.g. missing `PointerEvent`/`TouchEvent`), which would\n * otherwise log once per dispatched event in a suite that fires hundreds of them.\n */\nexport function warnOnce(key: string, msg: string): void {\n if (!isDev || warnedKeys.has(key)) return;\n\n warnedKeys.add(key);\n console.warn(`[@vielzeug/assay] ${msg}`);\n}\n\n/** @internal Test-only — clears \"warned once\" state so fallback-warning tests can run in any order. */\nexport function _resetWarnings(): void {\n warnedKeys.clear();\n}\n","/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\nimport { warnOnce } from './_dev';\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n warnOnce(\n 'pointer-fallback',\n 'PointerEvent is unavailable in this environment — falling back to MouseEvent. ' +\n 'pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.',\n );\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously. Every method dispatches an appropriately-typed\n * `Event` subclass with sensible defaults and returns `dispatchEvent`'s own boolean result\n * (`false` when a listener called `preventDefault()`) — the same contract for all of them,\n * with no exceptions.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom: (el: Element, name: string, opts?: CustomEventInit) =>\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts })),\n /** Dispatch any pre-built Event instance directly. */\n event: (el: Element, event: Event) => el.dispatchEvent(event),\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerCancel: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointercancel', { bubbles: true, cancelable: false, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerMove: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointermove', { bubbles: true, cancelable: true, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch: (el: Element, type: string, opts?: EventInit) => {\n if (typeof TouchEvent !== 'undefined') {\n return el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n\n warnOnce(\n 'touch-fallback',\n 'TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data.',\n );\n\n return el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n },\n} as const;\n","/**\n * DOM query helpers for test environments.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any DOM element — see {@link within} */\nexport interface QueryScope {\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Query a single element within `root`. Equivalent to `root.querySelector(selector)`. */\nexport function query<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E | null {\n return root.querySelector<E>(selector);\n}\n\n/** Query every element within `root` matching `selector`. */\nexport function queryAll<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E[] {\n return Array.from(root.querySelectorAll<E>(selector));\n}\n\n/** Query a single element within `root` by its `data-testid` attribute. */\nexport function queryByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E | null {\n return root.querySelector<E>(`[data-testid=\"${testId}\"]`);\n}\n\n/** Query every element within `root` matching a `data-testid` attribute. */\nexport function queryAllByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E[] {\n return Array.from(root.querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n}\n\nexport function queryByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E | null {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n}\n\nexport function queryAllByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E[] {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n}\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot?.querySelector<E>(selector) ?? null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return Array.from(host.shadowRoot?.querySelectorAll<E>(selector) ?? []);\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return queryInShadow<E>(host, `[part=\"${part}\"]`);\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every slotted child (elements\n * with no `slot` attribute) when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll<E>(`:scope > ${selector}`));\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to any element — useful for slotted/light DOM content.\n *\n * @example\n * const panel = fixture.query('.panel')!;\n * const { query } = within(panel);\n * expect(query('.title')?.textContent).toBe('Hello');\n */\nexport function within(element: Element): QueryScope {\n return {\n query: <E extends Element = Element>(selector: string) => query<E>(element, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(element, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(element, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(element, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(element, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(element, text, selector),\n };\n}\n","/**\n * Async waiting utilities for test environments.\n */\n\nimport { AssayTimeoutError } from './errors';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface WaitOptions {\n /** Maximum wait time in ms (default: 1000) */\n timeout?: number;\n /** Polling interval in ms (default: 50) */\n interval?: number;\n /** Message included in timeout error */\n message?: string;\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Poll until a callback returns truthy (or void) without throwing.\n * Supports both boolean conditions and `expect()` assertions.\n *\n * - Returns truthy → success\n * - Returns `undefined` (e.g. bare `expect()` call) → success\n * - Returns falsy value → retry\n * - Throws → retry\n *\n * Always rejects with `AssayTimeoutError` on timeout, regardless of which of the two retry\n * reasons above was last observed — `instanceof AssayTimeoutError` is a reliable way to catch\n * \"the condition never became true\" without caring whether the last attempt returned falsy or\n * threw. The original failure (an assertion's message, a thrown error) is preserved as `.cause`,\n * not merged into a mutated copy of it — `fn`'s thrown errors are caller-owned and never mutated.\n *\n * @example\n * await waitFor(() => query('.status')?.textContent === 'loaded');\n * await waitFor(() => expect(count).toBe(3));\n */\nexport async function waitFor(\n fn: () => unknown,\n { interval = 50, message, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n // Single loop, one `fn()` call site — always tries once more before checking the deadline,\n // so a condition that becomes true exactly as the timeout elapses still succeeds.\n for (;;) {\n try {\n const result = await fn();\n\n if (result === undefined || result) return;\n } catch (e) {\n lastError = e;\n }\n\n if (Date.now() >= deadline) break;\n\n await wait(interval);\n }\n\n // The default timing summary always appears — a custom `message` adds context in front of it\n // rather than replacing it, so a debugging session never loses \"how long did this actually wait\".\n const timing = `waitFor timed out after ${timeout}ms`;\n const base = message ? `${message} (${timing})` : timing;\n const cause = lastError instanceof Error ? lastError.message : lastError != null ? String(lastError) : undefined;\n\n throw new AssayTimeoutError(cause ? `${base}\\n${cause}` : base, { cause: lastError });\n}\n\n/**\n * Resolve when the target element emits the given event.\n *\n * @example\n * const promise = waitForEvent(el, 'change');\n * fire.click(trigger);\n * const event = await promise;\n */\nexport function waitForEvent<T extends Event = Event>(element: Element, name: string, timeout = 1000): Promise<T> {\n return new Promise((resolve, reject) => {\n const onEvent = (e: Event): void => {\n clearTimeout(timer);\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n element.removeEventListener(name, onEvent);\n reject(new AssayTimeoutError(`waitForEvent: \"${name}\" timed out after ${timeout}ms`));\n }, timeout);\n\n element.addEventListener(name, onEvent, { once: true });\n });\n}\n\n/**\n * Resolves after one microtask tick (`queueMicrotask`). Use to wait for reactivity (signal\n * effects, promise-chain continuations) to settle without moving into the macrotask queue —\n * cheaper and more precise than `wait(0)`, which yields to a full `setTimeout` turn instead.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Resolves after `ms` milliseconds. Prefer `nextTick()` for reactive updates and `waitFor()`\n * for polling a condition — reach for this only when a real macrotask delay is what the code\n * under test actually depends on (e.g. a debounce timer).\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"assay.iife.js","names":[],"sources":["../src/errors.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown when a required query has no match. */\nexport class AssayQueryError extends AssayError {}\n\n/** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */\nexport class AssayTimeoutError extends AssayError {}\n","/** Synchronous, exact DOM event dispatchers for test environments. */\n\n/** Dispatch an event instance without modifying it. */\nexport const dispatch = (target: EventTarget, event: Event): boolean => target.dispatchEvent(event);\n\nexport const fireBlur = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('blur', init));\n\nexport const fireChange = (target: EventTarget, init: EventInit = {}): boolean =>\n dispatch(target, new Event('change', { bubbles: true, ...init }));\n\nexport const fireClick = (target: EventTarget, init: MouseEventInit = {}): boolean =>\n dispatch(target, new MouseEvent('click', { bubbles: true, cancelable: true, ...init }));\n\nexport interface CustomEventOptions<T> extends CustomEventInit<T> {\n type: string;\n}\n\nexport const fireCustom = <T>(target: EventTarget, { type, ...init }: CustomEventOptions<T>): boolean =>\n dispatch(target, new CustomEvent(type, { bubbles: true, cancelable: true, composed: false, ...init }));\n\nexport const fireFocus = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('focus', init));\n\nexport const fireInput = (target: EventTarget, init: InputEventInit = {}): boolean =>\n dispatch(target, new InputEvent('input', { bubbles: true, ...init }));\n\nexport const fireKeyDown = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireKeyUp = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireSubmit = (target: EventTarget, init: SubmitEventInit = {}): boolean =>\n dispatch(target, new SubmitEvent('submit', { bubbles: true, cancelable: true, ...init }));\n","import { AssayQueryError } from './errors';\n\n/** DOM query helpers for test environments. */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any queryable DOM root — see {@link within}. */\nexport interface QueryScope {\n get<E extends Element = Element>(selector: string): E;\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n getByText<E extends Element = Element>(text: string, selector?: string): E;\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n getByTestId<E extends Element = Element>(testId: string): E;\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\ntype QueryRoot = ParentNode;\n\nconst describeRoot = (root: QueryRoot): string => {\n if (root instanceof Element) return root.outerHTML.slice(0, 500);\n\n if (root instanceof ShadowRoot) return `<shadow-root>${root.innerHTML.slice(0, 500)}</shadow-root>`;\n\n return root.nodeName;\n};\n\nconst requireMatch = <E extends Element>(element: E | null, description: string, root: QueryRoot): E => {\n if (element) return element;\n\n throw new AssayQueryError(`Unable to find ${description} within ${describeRoot(root)}.`);\n};\n\nconst query = <E extends Element = Element>(root: QueryRoot, selector: string): E | null =>\n root.querySelector<E>(selector);\n\nconst queryAll = <E extends Element = Element>(root: QueryRoot, selector: string): E[] => {\n return Array.from(root.querySelectorAll<E>(selector));\n};\n\nconst queryByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E | null =>\n queryAll<E>(root, '[data-testid]').find((element) => element.getAttribute('data-testid') === testId) ?? null;\n\nconst queryAllByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E[] =>\n queryAll<E>(root, '[data-testid]').filter((element) => element.getAttribute('data-testid') === testId);\n\nconst queryByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E | null => {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n};\n\nconst queryAllByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E[] => {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n};\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot ? query<E>(host.shadowRoot, selector) : null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return host.shadowRoot ? queryAll<E>(host.shadowRoot, selector) : [];\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return (\n queryAllInShadow<E>(host, '[part]').find((element) => element.getAttribute('part')?.split(/\\s+/).includes(part)) ??\n null\n );\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every child assigned to the\n * default slot when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n return Array.from(host.children).filter((element) =>\n slotName === undefined ? !element.getAttribute('slot') : element.getAttribute('slot') === slotName,\n ) as E[];\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to a DOM root — useful for light DOM and shadow roots.\n *\n * @example\n * const view = within(fixture.shadow!);\n * expect(view.get('.title').textContent).toBe('Hello');\n */\nexport function within(root: QueryRoot): QueryScope {\n return {\n get: <E extends Element = Element>(selector: string) =>\n requireMatch(query<E>(root, selector), `\"${selector}\"`, root),\n getByTestId: <E extends Element = Element>(testId: string) =>\n requireMatch(queryByTestId<E>(root, testId), `data-testid \"${testId}\"`, root),\n getByText: <E extends Element = Element>(text: string, selector = '*') =>\n requireMatch(queryByText<E>(root, text, selector), `text \"${text}\" matching \"${selector}\"`, root),\n query: <E extends Element = Element>(selector: string) => query<E>(root, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(root, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(root, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(root, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(root, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(root, text, selector),\n };\n}\n","import { AssayTimeoutError } from './errors';\n\nexport interface WaitOptions {\n /** Cancel the pending wait. */\n signal?: AbortSignal;\n /** Maximum wait time in ms (default: 1000). */\n timeout?: number;\n /** Polling interval in ms (default: 50). */\n interval?: number;\n}\n\nexport interface RetryOptions extends WaitOptions {\n /** Context included in the timeout error. */\n message?: string;\n}\n\nexport interface DelayOptions {\n /** Cancel the pending delay. */\n signal?: AbortSignal;\n}\n\nconst abortReason = (signal: AbortSignal): unknown =>\n signal.reason ?? new DOMException('The operation was aborted.', 'AbortError');\n\n/**\n * Resolve after `ms` milliseconds. Rejects with the abort reason when cancelled.\n */\nexport const delay = (ms = 0, { signal }: DelayOptions = {}): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const timer = setTimeout(done, ms);\n\n function done(): void {\n signal?.removeEventListener('abort', abort);\n resolve();\n }\n\n function abort(): void {\n clearTimeout(timer);\n reject(abortReason(signal!));\n }\n\n signal?.addEventListener('abort', abort, { once: true });\n });\n\n/** Poll until a synchronous or asynchronous predicate returns `true`. */\nexport async function waitUntil(\n predicate: () => boolean | Promise<boolean>,\n { interval = 50, signal, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n if (await predicate()) return;\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n throw new AssayTimeoutError(`waitUntil timed out after ${timeout}ms`);\n}\n\n/** Retry an assertion until it stops throwing. */\nexport async function retry(\n assertion: () => void | Promise<void>,\n { interval = 50, message, signal, timeout = 1000 }: RetryOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n try {\n await assertion();\n\n return;\n } catch (error) {\n lastError = error;\n }\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n const timing = `retry timed out after ${timeout}ms`;\n const context = message ? `${message} (${timing})` : timing;\n const detail = lastError instanceof Error ? lastError.message : lastError == null ? '' : String(lastError);\n\n throw new AssayTimeoutError(detail ? `${context}\\n${detail}` : context, { cause: lastError });\n}\n\n/** Resolve with the next event of `type`, or reject when it times out or is aborted. */\nexport function waitForEvent<T extends Event = Event>(\n target: EventTarget,\n type: string,\n { signal, timeout = 1000 }: Omit<WaitOptions, 'interval'> = {},\n): Promise<T> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const dispose = () => {\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n target.removeEventListener(type, onEvent);\n };\n const onEvent = (e: Event): void => {\n dispose();\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n dispose();\n reject(new AssayTimeoutError(`waitForEvent: \"${type}\" timed out after ${timeout}ms`));\n }, timeout);\n const onAbort = () => {\n dispose();\n reject(abortReason(signal!));\n };\n\n target.addEventListener(type, onEvent, { once: true });\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Resolve after one microtask tick. */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n"],"mappings":"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"}
package/dist/assay.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=!globalThis.__ASSAY_PROD__,r=new Set;function i(e,t){!n||r.has(e)||(r.add(e),console.warn(`[@vielzeug/assay] ${t}`))}var a=(e,t={})=>typeof PointerEvent<`u`?new PointerEvent(e,t):(i(`pointer-fallback`,`PointerEvent is unavailable in this environment — falling back to MouseEvent. pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.`),new MouseEvent(e,t)),o={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom:(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),event:(e,t)=>e.dispatchEvent(t),focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerCancel:(e,t)=>e.dispatchEvent(a(`pointercancel`,{bubbles:!0,cancelable:!1,...t})),pointerDown:(e,t)=>e.dispatchEvent(a(`pointerdown`,{bubbles:!0,cancelable:!0,...t})),pointerEnter:(e,t)=>e.dispatchEvent(a(`pointerenter`,{bubbles:!1,...t})),pointerLeave:(e,t)=>e.dispatchEvent(a(`pointerleave`,{bubbles:!1,...t})),pointerMove:(e,t)=>e.dispatchEvent(a(`pointermove`,{bubbles:!0,cancelable:!0,...t})),pointerUp:(e,t)=>e.dispatchEvent(a(`pointerup`,{bubbles:!0,cancelable:!0,...t})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch:(e,t,n)=>typeof TouchEvent<`u`?e.dispatchEvent(new TouchEvent(t,{bubbles:!0,cancelable:!0,...n})):(i(`touch-fallback`,"TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data."),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,...n})))};function s(e,t){return e.querySelector(t)}function c(e,t){return Array.from(e.querySelectorAll(t))}function l(e,t){return e.querySelector(`[data-testid="${t}"]`)}function u(e,t){return Array.from(e.querySelectorAll(`[data-testid="${t}"]`))}function d(e,t,n){for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null}function f(e,t,n){return Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t)}function p(e,t){return e.shadowRoot?.querySelector(t)??null}function m(e,t){return Array.from(e.shadowRoot?.querySelectorAll(t)??[])}function h(e,t){return p(e,`[part="${t}"]`)}function g(e,t){let n=t?`[slot="${t}"]`:`:not([slot])`;return Array.from(e.querySelectorAll(`:scope > ${n}`))}function _(e){return{query:t=>s(e,t),queryAll:t=>c(e,t),queryAllByTestId:t=>u(e,t),queryAllByText:(t,n=`*`)=>f(e,t,n),queryByTestId:t=>l(e,t),queryByText:(t,n=`*`)=>d(e,t,n)}}async function v(e,{interval:n=50,message:r,timeout:i=1e3}={}){let a=Date.now()+i,o;for(;;){try{let t=await e();if(t===void 0||t)return}catch(e){o=e}if(Date.now()>=a)break;await x(n)}let s=`waitFor timed out after ${i}ms`,c=r?`${r} (${s})`:s,l=o instanceof Error?o.message:o==null?void 0:String(o);throw new t(l?`${c}\n${l}`:c,{cause:o})}function y(e,n,r=1e3){return new Promise((i,a)=>{let o=e=>{clearTimeout(s),i(e)},s=setTimeout(()=>{e.removeEventListener(n,o),a(new t(`waitForEvent: "${n}" timed out after ${r}ms`))},r);e.addEventListener(n,o,{once:!0})})}var b=()=>new Promise(e=>{queueMicrotask(e)}),x=(e=0)=>new Promise(t=>{setTimeout(t,e)});export{e as AssayError,t as AssayTimeoutError,a as createPointerEvent,o as fire,g as getSlotted,b as nextTick,s as query,c as queryAll,u as queryAllByTestId,f as queryAllByText,m as queryAllInShadow,l as queryByTestId,d as queryByText,p as queryInShadow,h as queryPart,x as wait,v as waitFor,y as waitForEvent,_ as within};
1
+ var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{},r=(e,t)=>e.dispatchEvent(t),i=(e,t={})=>r(e,new FocusEvent(`blur`,t)),a=(e,t={})=>r(e,new Event(`change`,{bubbles:!0,...t})),o=(e,t={})=>r(e,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),s=(e,{type:t,...n})=>r(e,new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),c=(e,t={})=>r(e,new FocusEvent(`focus`,t)),l=(e,t={})=>r(e,new InputEvent(`input`,{bubbles:!0,...t})),u=(e,t={})=>r(e,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),d=(e,t={})=>r(e,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),f=(e,t={})=>r(e,new SubmitEvent(`submit`,{bubbles:!0,cancelable:!0,...t})),p=e=>e instanceof Element?e.outerHTML.slice(0,500):e instanceof ShadowRoot?`<shadow-root>${e.innerHTML.slice(0,500)}</shadow-root>`:e.nodeName,m=(e,n,r)=>{if(e)return e;throw new t(`Unable to find ${n} within ${p(r)}.`)},h=(e,t)=>e.querySelector(t),g=(e,t)=>Array.from(e.querySelectorAll(t)),_=(e,t)=>g(e,`[data-testid]`).find(e=>e.getAttribute(`data-testid`)===t)??null,v=(e,t)=>g(e,`[data-testid]`).filter(e=>e.getAttribute(`data-testid`)===t),y=(e,t,n=`*`)=>{for(let r of e.querySelectorAll(n))if(r.textContent?.trim()===t)return r;return null},b=(e,t,n=`*`)=>Array.from(e.querySelectorAll(n)).filter(e=>e.textContent?.trim()===t);function x(e,t){return e.shadowRoot?h(e.shadowRoot,t):null}function S(e,t){return e.shadowRoot?g(e.shadowRoot,t):[]}function C(e,t){return S(e,`[part]`).find(e=>e.getAttribute(`part`)?.split(/\s+/).includes(t))??null}function w(e,t){return Array.from(e.children).filter(e=>t===void 0?!e.getAttribute(`slot`):e.getAttribute(`slot`)===t)}function T(e){return{get:t=>m(h(e,t),`"${t}"`,e),getByTestId:t=>m(_(e,t),`data-testid "${t}"`,e),getByText:(t,n=`*`)=>m(y(e,t,n),`text "${t}" matching "${n}"`,e),query:t=>h(e,t),queryAll:t=>g(e,t),queryAllByTestId:t=>v(e,t),queryAllByText:(t,n=`*`)=>b(e,t,n),queryByTestId:t=>_(e,t),queryByText:(t,n=`*`)=>y(e,t,n)}}var E=e=>e.reason??new DOMException(`The operation was aborted.`,`AbortError`),D=(e=0,{signal:t}={})=>new Promise((n,r)=>{if(t?.aborted){r(E(t));return}let i=setTimeout(a,e);function a(){t?.removeEventListener(`abort`,o),n()}function o(){clearTimeout(i),r(E(t))}t?.addEventListener(`abort`,o,{once:!0})});async function O(e,{interval:t=50,signal:r,timeout:i=1e3}={}){let a=Date.now()+i;for(;;){if(r?.aborted)throw E(r);if(await e())return;if(Date.now()>=a)break;await D(t,{signal:r})}throw new n(`waitUntil timed out after ${i}ms`)}async function k(e,{interval:t=50,message:r,signal:i,timeout:a=1e3}={}){let o=Date.now()+a,s;for(;;){if(i?.aborted)throw E(i);try{await e();return}catch(e){s=e}if(Date.now()>=o)break;await D(t,{signal:i})}let c=`retry timed out after ${a}ms`,l=r?`${r} (${c})`:c,u=s instanceof Error?s.message:s==null?``:String(s);throw new n(u?`${l}\n${u}`:l,{cause:s})}function A(e,t,{signal:r,timeout:i=1e3}={}){return new Promise((a,o)=>{if(r?.aborted){o(E(r));return}let s=()=>{clearTimeout(l),r?.removeEventListener(`abort`,u),e.removeEventListener(t,c)},c=e=>{s(),a(e)},l=setTimeout(()=>{s(),o(new n(`waitForEvent: "${t}" timed out after ${i}ms`))},i),u=()=>{s(),o(E(r))};e.addEventListener(t,c,{once:!0}),r?.addEventListener(`abort`,u,{once:!0})})}var j=()=>new Promise(e=>{queueMicrotask(e)});export{e as AssayError,t as AssayQueryError,n as AssayTimeoutError,D as delay,r as dispatch,i as fireBlur,a as fireChange,o as fireClick,s as fireCustom,c as fireFocus,l as fireInput,u as fireKeyDown,d as fireKeyUp,f as fireSubmit,w as getSlotted,j as nextTick,S as queryAllInShadow,x as queryInShadow,C as queryPart,k as retry,A as waitForEvent,O as waitUntil,T as within};
2
2
  //# sourceMappingURL=assay.js.map
package/dist/assay.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"assay.js","names":[],"sources":["../src/errors.ts","../src/_dev.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */\nexport class AssayTimeoutError extends AssayError {}\n","const isDev = !(globalThis as { __ASSAY_PROD__?: boolean }).__ASSAY_PROD__;\n\nconst warnedKeys = new Set<string>();\n\n/**\n * @internal Warn at most once per `key` for the lifetime of the module — used for\n * environment-capability fallbacks (e.g. missing `PointerEvent`/`TouchEvent`), which would\n * otherwise log once per dispatched event in a suite that fires hundreds of them.\n */\nexport function warnOnce(key: string, msg: string): void {\n if (!isDev || warnedKeys.has(key)) return;\n\n warnedKeys.add(key);\n console.warn(`[@vielzeug/assay] ${msg}`);\n}\n\n/** @internal Test-only — clears \"warned once\" state so fallback-warning tests can run in any order. */\nexport function _resetWarnings(): void {\n warnedKeys.clear();\n}\n","/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\nimport { warnOnce } from './_dev';\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n warnOnce(\n 'pointer-fallback',\n 'PointerEvent is unavailable in this environment — falling back to MouseEvent. ' +\n 'pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.',\n );\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously. Every method dispatches an appropriately-typed\n * `Event` subclass with sensible defaults and returns `dispatchEvent`'s own boolean result\n * (`false` when a listener called `preventDefault()`) — the same contract for all of them,\n * with no exceptions.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom: (el: Element, name: string, opts?: CustomEventInit) =>\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts })),\n /** Dispatch any pre-built Event instance directly. */\n event: (el: Element, event: Event) => el.dispatchEvent(event),\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerCancel: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointercancel', { bubbles: true, cancelable: false, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerMove: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointermove', { bubbles: true, cancelable: true, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch: (el: Element, type: string, opts?: EventInit) => {\n if (typeof TouchEvent !== 'undefined') {\n return el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n\n warnOnce(\n 'touch-fallback',\n 'TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data.',\n );\n\n return el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n },\n} as const;\n","/**\n * DOM query helpers for test environments.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any DOM element — see {@link within} */\nexport interface QueryScope {\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Query a single element within `root`. Equivalent to `root.querySelector(selector)`. */\nexport function query<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E | null {\n return root.querySelector<E>(selector);\n}\n\n/** Query every element within `root` matching `selector`. */\nexport function queryAll<E extends Element = Element>(root: Element | ShadowRoot, selector: string): E[] {\n return Array.from(root.querySelectorAll<E>(selector));\n}\n\n/** Query a single element within `root` by its `data-testid` attribute. */\nexport function queryByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E | null {\n return root.querySelector<E>(`[data-testid=\"${testId}\"]`);\n}\n\n/** Query every element within `root` matching a `data-testid` attribute. */\nexport function queryAllByTestId<E extends Element = Element>(root: Element | ShadowRoot, testId: string): E[] {\n return Array.from(root.querySelectorAll<E>(`[data-testid=\"${testId}\"]`));\n}\n\nexport function queryByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E | null {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n}\n\nexport function queryAllByText<E extends Element = Element>(\n root: Element | ShadowRoot,\n text: string,\n selector: string,\n): E[] {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n}\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot?.querySelector<E>(selector) ?? null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return Array.from(host.shadowRoot?.querySelectorAll<E>(selector) ?? []);\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return queryInShadow<E>(host, `[part=\"${part}\"]`);\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every slotted child (elements\n * with no `slot` attribute) when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n const selector = slotName ? `[slot=\"${slotName}\"]` : ':not([slot])';\n\n return Array.from(host.querySelectorAll<E>(`:scope > ${selector}`));\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to any element — useful for slotted/light DOM content.\n *\n * @example\n * const panel = fixture.query('.panel')!;\n * const { query } = within(panel);\n * expect(query('.title')?.textContent).toBe('Hello');\n */\nexport function within(element: Element): QueryScope {\n return {\n query: <E extends Element = Element>(selector: string) => query<E>(element, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(element, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(element, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(element, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(element, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(element, text, selector),\n };\n}\n","/**\n * Async waiting utilities for test environments.\n */\n\nimport { AssayTimeoutError } from './errors';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface WaitOptions {\n /** Maximum wait time in ms (default: 1000) */\n timeout?: number;\n /** Polling interval in ms (default: 50) */\n interval?: number;\n /** Message included in timeout error */\n message?: string;\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Poll until a callback returns truthy (or void) without throwing.\n * Supports both boolean conditions and `expect()` assertions.\n *\n * - Returns truthy → success\n * - Returns `undefined` (e.g. bare `expect()` call) → success\n * - Returns falsy value → retry\n * - Throws → retry\n *\n * Always rejects with `AssayTimeoutError` on timeout, regardless of which of the two retry\n * reasons above was last observed — `instanceof AssayTimeoutError` is a reliable way to catch\n * \"the condition never became true\" without caring whether the last attempt returned falsy or\n * threw. The original failure (an assertion's message, a thrown error) is preserved as `.cause`,\n * not merged into a mutated copy of it — `fn`'s thrown errors are caller-owned and never mutated.\n *\n * @example\n * await waitFor(() => query('.status')?.textContent === 'loaded');\n * await waitFor(() => expect(count).toBe(3));\n */\nexport async function waitFor(\n fn: () => unknown,\n { interval = 50, message, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n // Single loop, one `fn()` call site — always tries once more before checking the deadline,\n // so a condition that becomes true exactly as the timeout elapses still succeeds.\n for (;;) {\n try {\n const result = await fn();\n\n if (result === undefined || result) return;\n } catch (e) {\n lastError = e;\n }\n\n if (Date.now() >= deadline) break;\n\n await wait(interval);\n }\n\n // The default timing summary always appears — a custom `message` adds context in front of it\n // rather than replacing it, so a debugging session never loses \"how long did this actually wait\".\n const timing = `waitFor timed out after ${timeout}ms`;\n const base = message ? `${message} (${timing})` : timing;\n const cause = lastError instanceof Error ? lastError.message : lastError != null ? String(lastError) : undefined;\n\n throw new AssayTimeoutError(cause ? `${base}\\n${cause}` : base, { cause: lastError });\n}\n\n/**\n * Resolve when the target element emits the given event.\n *\n * @example\n * const promise = waitForEvent(el, 'change');\n * fire.click(trigger);\n * const event = await promise;\n */\nexport function waitForEvent<T extends Event = Event>(element: Element, name: string, timeout = 1000): Promise<T> {\n return new Promise((resolve, reject) => {\n const onEvent = (e: Event): void => {\n clearTimeout(timer);\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n element.removeEventListener(name, onEvent);\n reject(new AssayTimeoutError(`waitForEvent: \"${name}\" timed out after ${timeout}ms`));\n }, timeout);\n\n element.addEventListener(name, onEvent, { once: true });\n });\n}\n\n/**\n * Resolves after one microtask tick (`queueMicrotask`). Use to wait for reactivity (signal\n * effects, promise-chain continuations) to settle without moving into the macrotask queue —\n * cheaper and more precise than `wait(0)`, which yields to a full `setTimeout` turn instead.\n */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n\n/**\n * Resolves after `ms` milliseconds. Prefer `nextTick()` for reactive updates and `waitFor()`\n * for polling a condition — reach for this only when a real macrotask delay is what the code\n * under test actually depends on (e.g. a debounce timer).\n */\nexport const wait = (ms = 0): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"assay.js","names":[],"sources":["../src/errors.ts","../src/events.ts","../src/query.ts","../src/wait.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown when a required query has no match. */\nexport class AssayQueryError extends AssayError {}\n\n/** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */\nexport class AssayTimeoutError extends AssayError {}\n","/** Synchronous, exact DOM event dispatchers for test environments. */\n\n/** Dispatch an event instance without modifying it. */\nexport const dispatch = (target: EventTarget, event: Event): boolean => target.dispatchEvent(event);\n\nexport const fireBlur = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('blur', init));\n\nexport const fireChange = (target: EventTarget, init: EventInit = {}): boolean =>\n dispatch(target, new Event('change', { bubbles: true, ...init }));\n\nexport const fireClick = (target: EventTarget, init: MouseEventInit = {}): boolean =>\n dispatch(target, new MouseEvent('click', { bubbles: true, cancelable: true, ...init }));\n\nexport interface CustomEventOptions<T> extends CustomEventInit<T> {\n type: string;\n}\n\nexport const fireCustom = <T>(target: EventTarget, { type, ...init }: CustomEventOptions<T>): boolean =>\n dispatch(target, new CustomEvent(type, { bubbles: true, cancelable: true, composed: false, ...init }));\n\nexport const fireFocus = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('focus', init));\n\nexport const fireInput = (target: EventTarget, init: InputEventInit = {}): boolean =>\n dispatch(target, new InputEvent('input', { bubbles: true, ...init }));\n\nexport const fireKeyDown = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireKeyUp = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireSubmit = (target: EventTarget, init: SubmitEventInit = {}): boolean =>\n dispatch(target, new SubmitEvent('submit', { bubbles: true, cancelable: true, ...init }));\n","import { AssayQueryError } from './errors';\n\n/** DOM query helpers for test environments. */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Scoped query helpers for any queryable DOM root — see {@link within}. */\nexport interface QueryScope {\n get<E extends Element = Element>(selector: string): E;\n query<E extends Element = Element>(selector: string): E | null;\n queryAll<E extends Element = Element>(selector: string): E[];\n getByText<E extends Element = Element>(text: string, selector?: string): E;\n queryByText<E extends Element = Element>(text: string, selector?: string): E | null;\n queryAllByText<E extends Element = Element>(text: string, selector?: string): E[];\n getByTestId<E extends Element = Element>(testId: string): E;\n queryByTestId<E extends Element = Element>(testId: string): E | null;\n queryAllByTestId<E extends Element = Element>(testId: string): E[];\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\ntype QueryRoot = ParentNode;\n\nconst describeRoot = (root: QueryRoot): string => {\n if (root instanceof Element) return root.outerHTML.slice(0, 500);\n\n if (root instanceof ShadowRoot) return `<shadow-root>${root.innerHTML.slice(0, 500)}</shadow-root>`;\n\n return root.nodeName;\n};\n\nconst requireMatch = <E extends Element>(element: E | null, description: string, root: QueryRoot): E => {\n if (element) return element;\n\n throw new AssayQueryError(`Unable to find ${description} within ${describeRoot(root)}.`);\n};\n\nconst query = <E extends Element = Element>(root: QueryRoot, selector: string): E | null =>\n root.querySelector<E>(selector);\n\nconst queryAll = <E extends Element = Element>(root: QueryRoot, selector: string): E[] => {\n return Array.from(root.querySelectorAll<E>(selector));\n};\n\nconst queryByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E | null =>\n queryAll<E>(root, '[data-testid]').find((element) => element.getAttribute('data-testid') === testId) ?? null;\n\nconst queryAllByTestId = <E extends Element = Element>(root: QueryRoot, testId: string): E[] =>\n queryAll<E>(root, '[data-testid]').filter((element) => element.getAttribute('data-testid') === testId);\n\nconst queryByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E | null => {\n for (const el of root.querySelectorAll<E>(selector)) {\n if (el.textContent?.trim() === text) return el;\n }\n\n return null;\n};\n\nconst queryAllByText = <E extends Element = Element>(root: QueryRoot, text: string, selector = '*'): E[] => {\n return Array.from(root.querySelectorAll<E>(selector)).filter((el) => el.textContent?.trim() === text);\n};\n\n/**\n * Queries the shadow root of a custom element for a matching CSS selector.\n * Returns `null` if the element has no shadow root or no match is found — safe to call on\n * both open-shadow custom elements and plain elements without checking `shadowRoot` first.\n */\nexport function queryInShadow<E extends Element = Element>(host: Element, selector: string): E | null {\n return host.shadowRoot ? query<E>(host.shadowRoot, selector) : null;\n}\n\n/** Queries all matching elements inside the shadow root of a custom element. */\nexport function queryAllInShadow<E extends Element = Element>(host: Element, selector: string): E[] {\n return host.shadowRoot ? queryAll<E>(host.shadowRoot, selector) : [];\n}\n\n/**\n * Queries a shadow DOM element by its CSS `part` attribute — shorthand for\n * `queryInShadow(host, '[part=\"name\"]')`.\n *\n * @example\n * const btn = queryPart(carousel, 'prev-btn');\n * expect(btn).not.toBeNull();\n */\nexport function queryPart<E extends Element = Element>(host: Element, part: string): E | null {\n return (\n queryAllInShadow<E>(host, '[part]').find((element) => element.getAttribute('part')?.split(/\\s+/).includes(part)) ??\n null\n );\n}\n\n/**\n * Returns the light-DOM children assigned to a named slot, or every child assigned to the\n * default slot when no name is given.\n *\n * @example\n * const slides = getSlotted(carousel);\n * expect(slides).toHaveLength(3);\n */\nexport function getSlotted<E extends Element = Element>(host: Element, slotName?: string): E[] {\n return Array.from(host.children).filter((element) =>\n slotName === undefined ? !element.getAttribute('slot') : element.getAttribute('slot') === slotName,\n ) as E[];\n}\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Create query helpers scoped to a DOM root — useful for light DOM and shadow roots.\n *\n * @example\n * const view = within(fixture.shadow!);\n * expect(view.get('.title').textContent).toBe('Hello');\n */\nexport function within(root: QueryRoot): QueryScope {\n return {\n get: <E extends Element = Element>(selector: string) =>\n requireMatch(query<E>(root, selector), `\"${selector}\"`, root),\n getByTestId: <E extends Element = Element>(testId: string) =>\n requireMatch(queryByTestId<E>(root, testId), `data-testid \"${testId}\"`, root),\n getByText: <E extends Element = Element>(text: string, selector = '*') =>\n requireMatch(queryByText<E>(root, text, selector), `text \"${text}\" matching \"${selector}\"`, root),\n query: <E extends Element = Element>(selector: string) => query<E>(root, selector),\n queryAll: <E extends Element = Element>(selector: string) => queryAll<E>(root, selector),\n queryAllByTestId: <E extends Element = Element>(testId: string) => queryAllByTestId<E>(root, testId),\n queryAllByText: <E extends Element = Element>(text: string, selector = '*') =>\n queryAllByText<E>(root, text, selector),\n queryByTestId: <E extends Element = Element>(testId: string) => queryByTestId<E>(root, testId),\n queryByText: <E extends Element = Element>(text: string, selector = '*') => queryByText<E>(root, text, selector),\n };\n}\n","import { AssayTimeoutError } from './errors';\n\nexport interface WaitOptions {\n /** Cancel the pending wait. */\n signal?: AbortSignal;\n /** Maximum wait time in ms (default: 1000). */\n timeout?: number;\n /** Polling interval in ms (default: 50). */\n interval?: number;\n}\n\nexport interface RetryOptions extends WaitOptions {\n /** Context included in the timeout error. */\n message?: string;\n}\n\nexport interface DelayOptions {\n /** Cancel the pending delay. */\n signal?: AbortSignal;\n}\n\nconst abortReason = (signal: AbortSignal): unknown =>\n signal.reason ?? new DOMException('The operation was aborted.', 'AbortError');\n\n/**\n * Resolve after `ms` milliseconds. Rejects with the abort reason when cancelled.\n */\nexport const delay = (ms = 0, { signal }: DelayOptions = {}): Promise<void> =>\n new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const timer = setTimeout(done, ms);\n\n function done(): void {\n signal?.removeEventListener('abort', abort);\n resolve();\n }\n\n function abort(): void {\n clearTimeout(timer);\n reject(abortReason(signal!));\n }\n\n signal?.addEventListener('abort', abort, { once: true });\n });\n\n/** Poll until a synchronous or asynchronous predicate returns `true`. */\nexport async function waitUntil(\n predicate: () => boolean | Promise<boolean>,\n { interval = 50, signal, timeout = 1000 }: WaitOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n if (await predicate()) return;\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n throw new AssayTimeoutError(`waitUntil timed out after ${timeout}ms`);\n}\n\n/** Retry an assertion until it stops throwing. */\nexport async function retry(\n assertion: () => void | Promise<void>,\n { interval = 50, message, signal, timeout = 1000 }: RetryOptions = {},\n): Promise<void> {\n const deadline = Date.now() + timeout;\n let lastError: unknown;\n\n for (;;) {\n if (signal?.aborted) throw abortReason(signal);\n\n try {\n await assertion();\n\n return;\n } catch (error) {\n lastError = error;\n }\n\n if (Date.now() >= deadline) break;\n\n await delay(interval, { signal });\n }\n\n const timing = `retry timed out after ${timeout}ms`;\n const context = message ? `${message} (${timing})` : timing;\n const detail = lastError instanceof Error ? lastError.message : lastError == null ? '' : String(lastError);\n\n throw new AssayTimeoutError(detail ? `${context}\\n${detail}` : context, { cause: lastError });\n}\n\n/** Resolve with the next event of `type`, or reject when it times out or is aborted. */\nexport function waitForEvent<T extends Event = Event>(\n target: EventTarget,\n type: string,\n { signal, timeout = 1000 }: Omit<WaitOptions, 'interval'> = {},\n): Promise<T> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n\n return;\n }\n\n const dispose = () => {\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n target.removeEventListener(type, onEvent);\n };\n const onEvent = (e: Event): void => {\n dispose();\n resolve(e as T);\n };\n const timer = setTimeout(() => {\n dispose();\n reject(new AssayTimeoutError(`waitForEvent: \"${type}\" timed out after ${timeout}ms`));\n }, timeout);\n const onAbort = () => {\n dispose();\n reject(abortReason(signal!));\n };\n\n target.addEventListener(type, onEvent, { once: true });\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Resolve after one microtask tick. */\nexport const nextTick = (): Promise<void> =>\n new Promise((resolve) => {\n queueMicrotask(resolve);\n });\n"],"mappings":"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"}
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{};exports.AssayError=e,exports.AssayTimeoutError=t;
1
+ var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{};exports.AssayError=e,exports.AssayQueryError=t,exports.AssayTimeoutError=n;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */\nexport class AssayTimeoutError extends AssayError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAuC,CAAW,CAAC"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown when a required query has no match. */\nexport class AssayQueryError extends AssayError {}\n\n/** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */\nexport class AssayTimeoutError extends AssayError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAqC,CAAW,CAAC,EAGpC,EAAb,cAAuC,CAAW,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -3,7 +3,10 @@ export declare class AssayError extends Error {
3
3
  constructor(message: string, opts?: ErrorOptions);
4
4
  static is(err: unknown): err is AssayError;
5
5
  }
6
- /** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */
6
+ /** Thrown when a required query has no match. */
7
+ export declare class AssayQueryError extends AssayError {
8
+ }
9
+ /** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */
7
10
  export declare class AssayTimeoutError extends AssayError {
8
11
  }
9
12
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED,yFAAyF;AACzF,qBAAa,iBAAkB,SAAQ,UAAU;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED,iDAAiD;AACjD,qBAAa,eAAgB,SAAQ,UAAU;CAAG;AAElD,6EAA6E;AAC7E,qBAAa,iBAAkB,SAAQ,UAAU;CAAG"}
package/dist/errors.js CHANGED
@@ -6,8 +6,8 @@ var e = class e extends Error {
6
6
  static is(t) {
7
7
  return t instanceof e;
8
8
  }
9
- }, t = class extends e {};
9
+ }, t = class extends e {}, n = class extends e {};
10
10
  //#endregion
11
- export { e as AssayError, t as AssayTimeoutError };
11
+ export { e as AssayError, t as AssayQueryError, n as AssayTimeoutError };
12
12
 
13
13
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown by `waitFor`/`waitForEvent` when a condition is not met within the timeout. */\nexport class AssayTimeoutError extends AssayError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAuC,EAAW,CAAC"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all Assay errors. Use `instanceof AssayError` to catch any Assay-originated error. */\nexport class AssayError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is AssayError {\n return err instanceof AssayError;\n }\n}\n\n/** Thrown when a required query has no match. */\nexport class AssayQueryError extends AssayError {}\n\n/** Thrown by `waitUntil`/`retry`/`waitForEvent` when the timeout elapses. */\nexport class AssayTimeoutError extends AssayError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAqC,EAAW,CAAC,GAGpC,IAAb,cAAuC,EAAW,CAAC"}
package/dist/events.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_dev.cjs");var t=(t,n={})=>typeof PointerEvent<`u`?new PointerEvent(t,n):(e.warnOnce(`pointer-fallback`,`PointerEvent is unavailable in this environment — falling back to MouseEvent. pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.`),new MouseEvent(t,n)),n={blur:(e,t)=>e.dispatchEvent(new FocusEvent(`blur`,{bubbles:!0,...t})),change:(e,t)=>e.dispatchEvent(new Event(`change`,{bubbles:!0,...t})),click:(e,t)=>e.dispatchEvent(new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...t})),custom:(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,cancelable:!0,composed:!1,...n})),event:(e,t)=>e.dispatchEvent(t),focus:(e,t,n)=>e.dispatchEvent(new FocusEvent(`focus`,{bubbles:!0,...n})),input:(e,t)=>e.dispatchEvent(new Event(`input`,{bubbles:!0,...t})),keyboard:(e,t,n)=>e.dispatchEvent(new KeyboardEvent(t,{bubbles:!0,cancelable:!0,...n})),keyDown:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...t})),keyUp:(e,t)=>e.dispatchEvent(new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...t})),mouse:(e,t,n)=>e.dispatchEvent(new MouseEvent(t,{bubbles:!0,cancelable:!0,...n})),pointerCancel:(e,n)=>e.dispatchEvent(t(`pointercancel`,{bubbles:!0,cancelable:!1,...n})),pointerDown:(e,n)=>e.dispatchEvent(t(`pointerdown`,{bubbles:!0,cancelable:!0,...n})),pointerEnter:(e,n)=>e.dispatchEvent(t(`pointerenter`,{bubbles:!1,...n})),pointerLeave:(e,n)=>e.dispatchEvent(t(`pointerleave`,{bubbles:!1,...n})),pointerMove:(e,n)=>e.dispatchEvent(t(`pointermove`,{bubbles:!0,cancelable:!0,...n})),pointerUp:(e,n)=>e.dispatchEvent(t(`pointerup`,{bubbles:!0,cancelable:!0,...n})),submit:(e,t)=>e.dispatchEvent(new Event(`submit`,{bubbles:!0,cancelable:!0,...t})),touch:(t,n,r)=>typeof TouchEvent<`u`?t.dispatchEvent(new TouchEvent(n,{bubbles:!0,cancelable:!0,...r})):(e.warnOnce(`touch-fallback`,"TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data."),t.dispatchEvent(new CustomEvent(n,{bubbles:!0,cancelable:!0,...r})))};exports.createPointerEvent=t,exports.fire=n;
1
+ var e=(e,t)=>e.dispatchEvent(t),t=(t,n={})=>e(t,new FocusEvent(`blur`,n)),n=(t,n={})=>e(t,new Event(`change`,{bubbles:!0,...n})),r=(t,n={})=>e(t,new MouseEvent(`click`,{bubbles:!0,cancelable:!0,...n})),i=(t,{type:n,...r})=>e(t,new CustomEvent(n,{bubbles:!0,cancelable:!0,composed:!1,...r})),a=(t,n={})=>e(t,new FocusEvent(`focus`,n)),o=(t,n={})=>e(t,new InputEvent(`input`,{bubbles:!0,...n})),s=(t,n={})=>e(t,new KeyboardEvent(`keydown`,{bubbles:!0,cancelable:!0,...n})),c=(t,n={})=>e(t,new KeyboardEvent(`keyup`,{bubbles:!0,cancelable:!0,...n})),l=(t,n={})=>e(t,new SubmitEvent(`submit`,{bubbles:!0,cancelable:!0,...n}));exports.dispatch=e,exports.fireBlur=t,exports.fireChange=n,exports.fireClick=r,exports.fireCustom=i,exports.fireFocus=a,exports.fireInput=o,exports.fireKeyDown=s,exports.fireKeyUp=c,exports.fireSubmit=l;
2
2
  //# sourceMappingURL=events.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.cjs","names":[],"sources":["../src/events.ts"],"sourcesContent":["/**\n * Synchronous DOM event dispatchers for test environments.\n */\n\nimport { warnOnce } from './_dev';\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport const createPointerEvent = (type: string, init: PointerEventInit = {}): Event => {\n if (typeof PointerEvent !== 'undefined') {\n return new PointerEvent(type, init);\n }\n\n warnOnce(\n 'pointer-fallback',\n 'PointerEvent is unavailable in this environment — falling back to MouseEvent. ' +\n 'pointerId/pointerType/isPrimary and similar pointer-only fields will be missing from every dispatched event.',\n );\n\n return new MouseEvent(type, init);\n};\n\n// ─── API ─────────────────────────────────────────────────────────────────────\n\n/**\n * Fire low-level DOM events synchronously. Every method dispatches an appropriately-typed\n * `Event` subclass with sensible defaults and returns `dispatchEvent`'s own boolean result\n * (`false` when a listener called `preventDefault()`) — the same contract for all of them,\n * with no exceptions.\n *\n * @example\n * fire.click(button);\n * fire.keyDown(input, { key: 'Enter' });\n * fire.custom(el, 'value-change', { detail: 42 });\n */\nexport const fire = {\n blur: (el: Element, opts?: FocusEventInit) => el.dispatchEvent(new FocusEvent('blur', { bubbles: true, ...opts })),\n change: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('change', { bubbles: true, ...opts })),\n click: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a CustomEvent. Pass `{ detail: ... }` for event detail. */\n custom: (el: Element, name: string, opts?: CustomEventInit) =>\n el.dispatchEvent(new CustomEvent(name, { bubbles: true, cancelable: true, composed: false, ...opts })),\n /** Dispatch any pre-built Event instance directly. */\n event: (el: Element, event: Event) => el.dispatchEvent(event),\n focus: (el: Element, _name?: string, opts?: FocusEventInit) =>\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true, ...opts })),\n input: (el: Element, opts?: EventInit) => el.dispatchEvent(new Event('input', { bubbles: true, ...opts })),\n /** Fire a KeyboardEvent with a given type (keydown, keyup, keypress). */\n keyboard: (el: Element, type: string, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, ...opts })),\n keyDown: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...opts })),\n keyUp: (el: Element, opts?: KeyboardEventInit) =>\n el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a MouseEvent with a given type (click, mousedown, etc.). */\n mouse: (el: Element, type: string, opts?: MouseEventInit) =>\n el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, ...opts })),\n pointerCancel: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointercancel', { bubbles: true, cancelable: false, ...opts })),\n pointerDown: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerdown', { bubbles: true, cancelable: true, ...opts })),\n pointerEnter: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerenter', { bubbles: false, ...opts })),\n pointerLeave: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerleave', { bubbles: false, ...opts })),\n pointerMove: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointermove', { bubbles: true, cancelable: true, ...opts })),\n pointerUp: (el: Element, opts?: PointerEventInit) =>\n el.dispatchEvent(createPointerEvent('pointerup', { bubbles: true, cancelable: true, ...opts })),\n submit: (el: Element, opts?: EventInit) =>\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true, ...opts })),\n /** Fire a touch event; falls back to CustomEvent in environments without TouchEvent. */\n touch: (el: Element, type: string, opts?: EventInit) => {\n if (typeof TouchEvent !== 'undefined') {\n return el.dispatchEvent(new TouchEvent(type, { bubbles: true, cancelable: true, ...opts }));\n }\n\n warnOnce(\n 'touch-fallback',\n 'TouchEvent is unavailable in this environment — falling back to CustomEvent. The dispatched event will have no `touches`/`changedTouches` data.',\n );\n\n return el.dispatchEvent(new CustomEvent(type, { bubbles: true, cancelable: true, ...opts }));\n },\n} as const;\n"],"mappings":"8BAQA,IAAa,GAAsB,EAAc,EAAyB,CAAC,IACrE,OAAO,aAAiB,IACnB,IAAI,aAAa,EAAM,CAAI,GAGpC,EAAA,SACE,mBACA,4LAEF,EAEO,IAAI,WAAW,EAAM,CAAI,GAgBrB,EAAO,CAClB,MAAO,EAAa,IAA0B,EAAG,cAAc,IAAI,WAAW,OAAQ,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACjH,QAAS,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAC3G,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAExF,QAAS,EAAa,EAAc,IAClC,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,GAAO,GAAG,CAAK,CAAC,CAAC,EAEvG,OAAQ,EAAa,IAAiB,EAAG,cAAc,CAAK,EAC5D,OAAQ,EAAa,EAAgB,IACnC,EAAG,cAAc,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EACtE,OAAQ,EAAa,IAAqB,EAAG,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAEzG,UAAW,EAAa,EAAc,IACpC,EAAG,cAAc,IAAI,cAAc,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACxF,SAAU,EAAa,IACrB,EAAG,cAAc,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAC7F,OAAQ,EAAa,IACnB,EAAG,cAAc,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE3F,OAAQ,EAAa,EAAc,IACjC,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EACrF,eAAgB,EAAa,IAC3B,EAAG,cAAc,EAAmB,gBAAiB,CAAE,QAAS,GAAM,WAAY,GAAO,GAAG,CAAK,CAAC,CAAC,EACrG,aAAc,EAAa,IACzB,EAAG,cAAc,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,cAAe,EAAa,IAC1B,EAAG,cAAc,EAAmB,eAAgB,CAAE,QAAS,GAAO,GAAG,CAAK,CAAC,CAAC,EAClF,aAAc,EAAa,IACzB,EAAG,cAAc,EAAmB,cAAe,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAClG,WAAY,EAAa,IACvB,EAAG,cAAc,EAAmB,YAAa,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAChG,QAAS,EAAa,IACpB,EAAG,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEpF,OAAQ,EAAa,EAAc,IAC7B,OAAO,WAAe,IACjB,EAAG,cAAc,IAAI,WAAW,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,GAG5F,EAAA,SACE,iBACA,iJACF,EAEO,EAAG,cAAc,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE/F"}
1
+ {"version":3,"file":"events.cjs","names":[],"sources":["../src/events.ts"],"sourcesContent":["/** Synchronous, exact DOM event dispatchers for test environments. */\n\n/** Dispatch an event instance without modifying it. */\nexport const dispatch = (target: EventTarget, event: Event): boolean => target.dispatchEvent(event);\n\nexport const fireBlur = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('blur', init));\n\nexport const fireChange = (target: EventTarget, init: EventInit = {}): boolean =>\n dispatch(target, new Event('change', { bubbles: true, ...init }));\n\nexport const fireClick = (target: EventTarget, init: MouseEventInit = {}): boolean =>\n dispatch(target, new MouseEvent('click', { bubbles: true, cancelable: true, ...init }));\n\nexport interface CustomEventOptions<T> extends CustomEventInit<T> {\n type: string;\n}\n\nexport const fireCustom = <T>(target: EventTarget, { type, ...init }: CustomEventOptions<T>): boolean =>\n dispatch(target, new CustomEvent(type, { bubbles: true, cancelable: true, composed: false, ...init }));\n\nexport const fireFocus = (target: EventTarget, init: FocusEventInit = {}): boolean =>\n dispatch(target, new FocusEvent('focus', init));\n\nexport const fireInput = (target: EventTarget, init: InputEventInit = {}): boolean =>\n dispatch(target, new InputEvent('input', { bubbles: true, ...init }));\n\nexport const fireKeyDown = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireKeyUp = (target: EventTarget, init: KeyboardEventInit = {}): boolean =>\n dispatch(target, new KeyboardEvent('keyup', { bubbles: true, cancelable: true, ...init }));\n\nexport const fireSubmit = (target: EventTarget, init: SubmitEventInit = {}): boolean =>\n dispatch(target, new SubmitEvent('submit', { bubbles: true, cancelable: true, ...init }));\n"],"mappings":"AAGA,IAAa,GAAY,EAAqB,IAA0B,EAAO,cAAc,CAAK,EAErF,GAAY,EAAqB,EAAuB,CAAC,IACpE,EAAS,EAAQ,IAAI,WAAW,OAAQ,CAAI,CAAC,EAElC,GAAc,EAAqB,EAAkB,CAAC,IACjE,EAAS,EAAQ,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAErD,GAAa,EAAqB,EAAuB,CAAC,IACrE,EAAS,EAAQ,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAM3E,GAAiB,EAAqB,CAAE,OAAM,GAAG,KAC5D,EAAS,EAAQ,IAAI,YAAY,EAAM,CAAE,QAAS,GAAM,WAAY,GAAM,SAAU,GAAO,GAAG,CAAK,CAAC,CAAC,EAE1F,GAAa,EAAqB,EAAuB,CAAC,IACrE,EAAS,EAAQ,IAAI,WAAW,QAAS,CAAI,CAAC,EAEnC,GAAa,EAAqB,EAAuB,CAAC,IACrE,EAAS,EAAQ,IAAI,WAAW,QAAS,CAAE,QAAS,GAAM,GAAG,CAAK,CAAC,CAAC,EAEzD,GAAe,EAAqB,EAA0B,CAAC,IAC1E,EAAS,EAAQ,IAAI,cAAc,UAAW,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAEhF,GAAa,EAAqB,EAA0B,CAAC,IACxE,EAAS,EAAQ,IAAI,cAAc,QAAS,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC,EAE9E,GAAc,EAAqB,EAAwB,CAAC,IACvE,EAAS,EAAQ,IAAI,YAAY,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,GAAG,CAAK,CAAC,CAAC"}