renderWithTap.
- Vitest
- Jest
document.documentElement.
Rendering a component
tap, mock and the queryClient it built.
Every storage call needs
storage:write, reads included. handler has to be built against the
test key set — createTappifyHandler({ ...handlerOptions, jwks: await testJwks() }) — and
extensionId has to name the same extension the handler does, because the mock signs the token it
sends and the handler checks the audience on it.
The mock
createTapMock() builds the same bridge without a component, for a test that has no UI. Storage
is an in-memory implementation of the collections you name, data queries answer from the sandbox
fixtures the portal preview uses, and everything else the host would do is recorded:
The mock drives the bridge from outside the component too:
setFilters takes a whole TapFilters; the filters option is the one that merges. setParams
takes the shape the host builds, which is positional — pageId, path, and one key per segment.
Matchers
expect(mock) rather than the component or tap.
Testing your server
createTestClient drives your handler the way Tappify does, with a signed token, so a procedure
is tested without a running Tappify:
createTappifyHandler verifies tokens
against Tappify’s live key set, so every call would come back 401. The client rebuilds the handler
around a test key set and signs against it.
The client has one method per route — health, metrics, tool, mention, action,
procedure, context, event, work — and raw(method, path, body) for the status codes.
signTestToken() and testJwks() are there when you verify tokens yourself.
Two validators read a response the way a reviewer would and return the list of what is wrong, so
assert the list is empty: validateMetricsResponse(response, metrics) against your declared
metric definitions, and validateToolResponse(value, returns) against the card shape a tool of
that returns kind has to answer with.
tappify extension doctor runs your test script and counts a
failing suite as a failed check.