/** * Test helpers for reaching a mounted module artifact's DOM. * * Since ADR 0040 the generic loader mounts every declared browser artifact inside its own Shadow DOM * root, so a module's elements are deliberately *not* in the host document's tree: `querySelector` * on the shell's container cannot see them, and neither can a stray Dashboard stylesheet. These * helpers walk into those open roots so a shell test can still assert on what a module rendered — * which is exactly what a host's own diagnostics would do, and is why the roots are `open`. * * Deliberately test-only. Production shell code never reaches into an artifact's root; the module * owns its contents. */ const ISOLATION_HOST_SELECTOR = "[data-ekklesia-module-ui]"; function isolatedRoots(root: ParentNode): readonly ShadowRoot[] { return [...root.querySelectorAll(ISOLATION_HOST_SELECTOR)] .map((host) => host.shadowRoot) .filter((shadowRoot): shadowRoot is ShadowRoot => shadowRoot !== null); } /** The first match anywhere in `root`, including inside every mounted artifact's isolated root. */ export function deepQuery(root: ParentNode, selector: string): Element | null { const direct = root.querySelector(selector); if (direct) { return direct; } for (const shadowRoot of isolatedRoots(root)) { const nested = deepQuery(shadowRoot, selector); if (nested) { return nested; } } return null; } /** Every match anywhere in `root`, host tree first, then each artifact's isolated root in order. */ export function deepQueryAll(root: ParentNode, selector: string): readonly Element[] { return [ ...root.querySelectorAll(selector), ...isolatedRoots(root).flatMap((shadowRoot) => [...deepQueryAll(shadowRoot, selector)]), ]; } /** The text an artifact rendered, read from the exact element the loader hands its `mount` — so the * stylesheet the loader installed alongside it is never mistaken for content. */ export function isolatedText(container: Element | null | undefined): string { return isolatedMountTarget(container)?.textContent ?? ""; } /** The element the loader passed to a mounted artifact's `mount`, for identity assertions. */ export function isolatedMountTarget(container: Element | null | undefined): Element | null { const host = container?.querySelector(ISOLATION_HOST_SELECTOR); return host?.shadowRoot?.querySelector("[data-ekklesia-module-ui-root]") ?? null; }