{"version":3,"file":"render-hook.cjs","names":[],"sources":["../../src/testing/render-hook.ts"],"sourcesContent":["/**\n * renderHook — test composable hooks (lifecycle, reactive state, etc.) in isolation\n * without rendering a full component template.\n */\n\nimport { createScope } from '@vielzeug/ripple';\n\nimport { OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createProps, type InferProps, type PropInputDefs } from '../props';\nimport { createRuntimeContext, onCleanup, runWithContext } from '../runtime';\nimport { flush } from './flush';\n\nexport interface HookFixture<T> {\n  /** Teardown the hook (runs cleanups). Idempotent. */\n  dispose(): void;\n  /** `true` after `dispose()` has been called. */\n  readonly disposed: boolean;\n  /** Flush pending reactive updates */\n  flush(): Promise<void>;\n  /** The value returned by the setup function */\n  result: T;\n  /** Delegates to `dispose()`. Enables `using` declarations. */\n  [Symbol.dispose](): void;\n}\n\nconst hookDisposers = new Set<() => void>();\n\n/** @internal Dispose every active hook fixture for test isolation. */\nexport const _disposeRenderHooks = (): void => {\n  for (const dispose of [...hookDisposers]) dispose();\n};\n\n/**\n * Run a setup function in a component-like context and return the result.\n * `onMounted`/`onCleanup`/`bind`/... work exactly as they would inside a real\n * `setup()`, since they resolve the same implicit current-component context.\n *\n * @example No props\n * ```ts\n * const { result, flush } = await renderHook(() => {\n *   const count = signal(0);\n *   onMounted(() => { count.value = 1; });\n *   return count;\n * });\n * expect(result.value).toBe(1);\n * ```\n *\n * @example With props\n * ```ts\n * const { result } = await renderHook(\n *   { label: prop.string('hello') },\n *   (props) => props.label,\n * );\n * expect(result.value).toBe('hello');\n * ```\n */\nexport async function renderHook<T>(setup: (_props: Record<never, never>) => T): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n  propDefs: D,\n  setup: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>>;\nexport async function renderHook<D extends PropInputDefs, T>(\n  setupOrDefs: ((_props: Record<never, never>) => T) | D,\n  maybeSetup?: (props: InferProps<D>) => T,\n): Promise<HookFixture<T>> {\n  const hasPropDefs = typeof setupOrDefs !== 'function';\n  const propDefs = hasPropDefs ? (setupOrDefs as D) : undefined;\n  const setup = hasPropDefs ? maybeSetup! : (setupOrDefs as (_props: Record<never, never>) => T);\n\n  const hostScope = createScope();\n  // Minimal host element so getHost() works inside setup\n  const hostEl = document.createElement('div');\n\n  document.body.appendChild(hostEl);\n\n  // Built via the runtime's own factory, so the harness can never desync from a\n  // new required RuntimeContext field. Note: `onFormReset()` registrations land\n  // in this context's formResetCallbacks but are never fired — renderHook has no\n  // ancestor <form>; test form-reset behaviour through mount() instead.\n  const ctx = createRuntimeContext(hostEl);\n  const { mountCallbacks } = ctx;\n\n  let result!: T;\n\n  try {\n    hostScope.run(() => {\n      runWithContext(ctx, () => {\n        const props = propDefs ? createProps(hostEl, propDefs as PropInputDefs) : ({} as Record<never, never>);\n\n        result = setup(props as InferProps<D>);\n      });\n    });\n\n    // Run mount callbacks (simulates connectedCallback → queueMicrotask)\n    for (const cb of mountCallbacks) {\n      try {\n        hostScope.run(() => {\n          runWithContext(ctx, () => {\n            const cleanup = cb();\n\n            if (typeof cleanup === 'function') onCleanup(cleanup);\n          });\n        });\n      } catch (error) {\n        // Mirror BaseElement's real onMounted error handling (see\n        // base-element.ts's `_scheduleMountCallbacks`): report the error\n        // (console + `ore:error` event on the host) and keep running the\n        // remaining callbacks, instead of hiding the failure entirely.\n        const err = error instanceof Error ? error : new Error(String(error));\n\n        reportRuntimeError(\n          new OreLifecycleError(`renderHook() mount callback failed`, {\n            cause: err,\n            component: 'renderHook',\n            phase: 'mounted',\n          }),\n          hostEl,\n        );\n      }\n    }\n  } catch (err) {\n    hostScope.dispose();\n    hostEl.remove();\n    throw err;\n  }\n\n  await flush();\n\n  let isDisposed = false;\n\n  function dispose() {\n    if (isDisposed) return;\n\n    isDisposed = true;\n    hostScope.dispose();\n    hostEl.remove();\n    hookDisposers.delete(dispose);\n  }\n\n  hookDisposers.add(dispose);\n\n  return {\n    dispose,\n\n    get disposed(): boolean {\n      return isDisposed;\n    },\n\n    async flush() {\n      await flush();\n    },\n\n    result,\n\n    [Symbol.dispose]() {\n      dispose();\n    },\n  };\n}\n"],"mappings":"kJAyBA,IAAM,EAAgB,IAAI,IAGb,MAAkC,CAC7C,IAAK,IAAM,IAAW,CAAC,GAAG,CAAa,EAAG,EAAQ,CACpD,EA+BA,eAAsB,EACpB,EACA,EACyB,CACzB,IAAM,EAAc,OAAO,GAAgB,WACrC,EAAW,EAAe,EAAoB,IAAA,GAC9C,EAAQ,EAAc,EAAe,EAErC,GAAA,EAAY,EAAA,YAAA,CAAY,EAExB,EAAS,SAAS,cAAc,KAAK,EAE3C,SAAS,KAAK,YAAY,CAAM,EAMhC,IAAM,EAAM,EAAA,qBAAqB,CAAM,EACjC,CAAE,kBAAmB,EAEvB,EAEJ,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAQ,EAAW,EAAA,YAAY,EAAQ,CAAyB,EAAK,CAAC,EAE5E,EAAS,EAAM,CAAsB,CACvC,CAAC,CACH,CAAC,EAGD,IAAK,IAAM,KAAM,EACf,GAAI,CACF,EAAU,QAAU,CAClB,EAAA,eAAe,MAAW,CACxB,IAAM,EAAU,EAAG,EAEf,OAAO,GAAY,YAAY,EAAA,UAAU,CAAO,CACtD,CAAC,CACH,CAAC,CACH,OAAS,EAAO,CAKd,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEpE,EAAA,mBACE,IAAI,EAAA,kBAAkB,qCAAsC,CAC1D,MAAO,EACP,UAAW,aACX,MAAO,SACT,CAAC,EACD,CACF,CACF,CAEJ,OAAS,EAAK,CAGZ,MAFA,EAAU,QAAQ,EAClB,EAAO,OAAO,EACR,CACR,CAEA,MAAM,EAAA,MAAM,EAEZ,IAAI,EAAa,GAEjB,SAAS,GAAU,CACb,IAEJ,EAAa,GACb,EAAU,QAAQ,EAClB,EAAO,OAAO,EACd,EAAc,OAAO,CAAO,EAC9B,CAIA,OAFA,EAAc,IAAI,CAAO,EAElB,CACL,UAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,MAAM,OAAQ,CACZ,MAAM,EAAA,MAAM,CACd,EAEA,SAEA,CAAC,OAAO,UAAW,CACjB,EAAQ,CACV,CACF,CACF"}