Testing Components
Mount, flush, and inspect Naos components with @naos-ui/testing.
@naos-ui/testing is the supported harness for component tests. It mounts a
compiled component, awaits the runtime's microtask flush, drives props and
attributes, captures typed events, and queries shadow content — including
named parts, which are the public styling contract.
Earlier repo examples wrote component state into document.body.dataset.*
probe attributes and read them back from Playwright. That pattern is
superseded: it couples tests to internal wiring and guesses at flush timing.
Use the harness instead.
Mounting
import { mount } from "@naos-ui/testing"
import "@naos-ui/primitives/button"
const component = await mount("naos-button", {
attrs: { "data-testid": "cta" },
props: { label: "Send" },
})
component.queryPart("control")?.textContent // "Send"
component.unmount()mount() creates the element (or accepts an existing one), applies initial
attributes and properties, appends it to a container in document.body, and
resolves after the runtime flush — the component's first render is complete
when the promise settles.
Driving Updates
await component.setProps({ label: "Submit", variant: "primary" })
await component.setAttrs({ "aria-label": "Submit", hidden: null })Both helpers await the flush, so assertions immediately after them observe the
updated DOM. null removes an attribute.
Scheduling
import { flush, nextTick } from "@naos-ui/testing"
await nextTick() // exactly one runtime scheduler turn
await flush() // drains cascaded updatesBoth are tied to the real runtime scheduler (scheduleNaosUpdate), so tests
never guess with setTimeout.
Events
const presses = component.capture<{ variant: string }>("naos-press")
component.queryPart<HTMLButtonElement>("control")?.click()
presses.count // 1
presses.last?.detail // { variant: "primary" }Captures are typed by their detail payload and disposed automatically on
unmount(). captureEvents(target, name) is available for arbitrary targets.
Queries
component.shadow()— the shadow root (throws for light-DOM components).component.query(selector)/queryAll(selector)— shadow-first queries.component.queryPart(name)— finds[part~=name], piercing nested shadow roots, so tests assert against the public part contract instead of internal markup.
Environment
The harness runs anywhere Custom Elements and Shadow DOM exist. In Vitest, use
the happy-dom environment:
// @vitest-environment happy-domor set --environment happy-dom on the test script.