Testing your integration
What to assert about a component you did not write. The stable contracts, the accessibility assertions worth copying, and the tests that break on any refactor.
The short version
NOT IMPLEMENTED. This component does not exist in any released version of opsinjs. There is no package to install, no module to import and no props interface to generate code against. Everything on this page is a specification of intended behaviour and may change without notice. Do not write code against it.
This component is not built yet
PlannedRoadmapWhat “planned” means
opsinjs has no test suite of its own. There is none, automated or manual,
for the design system itself; Accessibility for
testing records the same gap. What runs on
every change is static: check:generated, check:ia, check:a11y over
component source, check:llms, eslint and tsc. None of those mounts a
component. The nightly adds a browser half against the built /view routes,
checking hit area and survival at 1.3x and at 200%. That is a real measurement,
but it arrives the morning after a merge rather than before it.
So assume nothing underneath your composition is covered, and assert what matters to you. The list below is what you would want to assert about your composition regardless of who owns the layer beneath it, and until opsinjs has a suite it is also the only thing standing between a regression in a component and a reader.
Assert against contracts that are covered by semver:
- Roles and accessible names, queried with
getByRole("button", { name: "Save reading" }) - Data attributes, matched with
[data-status="urgent"]and[data-open] - Visible text is what the reader actually reads
Do not assert against class names, DOM structure, or the internal nesting of a compound component. Those change without a major version and your test suite becomes a change-detector rather than a safety net.
How it works
What is stable
| Stable, safe to assert | Not stable, do not assert |
|---|---|
| ARIA roles and accessible names | Generated class names |
data-* attributes (the vocabulary) | Element nesting depth |
| Visible text content | Internal element order |
| Keyboard behaviour | Inline style values |
| Focus position after an interaction | Portal container identity |
The accessibility assertions worth copying
These are the ones that catch real regressions in a health product, and they are cheap:
- Every interactive element has an accessible name. A whole-page sweep catches the icon button someone added last week.
- Focus goes where you said it goes. After opening a sheet, after a failed submit, after closing a dialog. Focus bugs are invisible until somebody without a mouse finds them.
- The error is associated with its control. The control's
aria-describedbyresolves to the message element, not merely a message somewhere on the page. aria-invalidclears when the error clears.- A status change is announced, i.e. it lands in a live region, rather than only appearing.
- The status word is present as text, not only as a colour. This is colour independence as a unit test.
- An automated accessibility scan on the rendered output. It finds a meaningful minority of issues; it does not find focus order, and it never finds wording.
Do this
- Query by role and name first, by text second, by test id last. The order reflects how close the query is to what a reader experiences.
- Test the flow, not the frame. For a pattern, assert the sequence: submit fails, summary appears, focus is on the summary, activating an entry focuses the field.
- Assert on the honest states. A stale value must not carry a status; an empty state must not render a zero. Those are the assertions that protect the reader rather than the code.
- Use fake timers deliberately for debounced validation, and assert on the final state rather than on intermediate ones.
- Test with
prefers-reduced-motionset. If a test fails only when motion is off, motion was carrying meaning. - Keep one end-to-end test per critical flow, such as logging a reading or disclosing a result. Let unit tests cover the rest.
Not this
- Do not snapshot markup. A DOM snapshot of a component you do not own turns every upstream improvement into a failing test and trains the team to update snapshots without reading them.
- Do not assert on class names. They are not a contract;
data-*attributes are. - Do not test that a library works. That a
Dialogtraps focus is Base UI's test to run, not yours. - Do not use
container.querySelectorto reach into a component's internals. If you need it, the component is missing an accessible affordance and that is the actual bug. - Do not mock the component under test. Mocking a
ResultCardto test a page tests nothing about the page's real behaviour. - Do not rely on an automated scan alone. It will pass a page whose reading order is nonsense.
Gotchas
- Portals render outside the container returned by your render helper. Queries scoped to it will not find a dialog's contents; query the document.
jsdomdoes not do layout. Anything about size, position, overflow, visibility-by-clipping or target size cannot be tested there. Those need a real browser.jsdomdoes not implementmatchMediaby default, so a component readingprefers-reduced-motionneeds it stubbed or it silently takes one branch.- Exit transitions keep an element mounted. A "closed" assertion immediately after a close will fail until the transition finishes; wait for removal.
- Fake timers and
user-eventinteract badly unless the timer configuration is passed through; the symptom is a test that hangs rather than fails. - An accessible name computed from
aria-labelledbyneeds the target to exist. A test that renders a fragment without it will report a missing name that is present in the real page.
Related
- Data attributes has the contracts that are safe to assert on.
- For testing is the accessibility test plan by role.
- Keyboard and focus has the global contract your focus assertions are checking.
- Component checklist says what opsinjs itself has to pass.
Performance and bundle size
What each component will cost, the composited-surface budget that matters more than kilobytes, and how to measure both rather than argue about them.
Tooling
Two lint plugins that turn the system's most important rules from advice into build failures. A rule enforced only by review is a rule enforced only sometimes.