Skip to content

Testing

Datastar Kit is easiest to test at the same boundary it runs at: native Request in, native Response out.

Handler tests

Prefer calling fetch-compatible handlers directly:

ts
const response = await app.fetch(
  new Request("http://test.local/todos/add", {
    method: "POST",
    body: JSON.stringify({ title: "Ship docs" })
  })
)

expect(response.status).toBe(204)

This keeps tests close to production behavior and avoids framework mocks for SDK-shaped code.

For Datastar actions, build a normal request with the same JSON signal payload the browser sends:

ts
const response = await app.fetch(
  new Request("http://test.local/todos", {
    method: "POST",
    headers: { "datastar-request": "true" },
    body: JSON.stringify({ title: "Ship docs" })
  })
)

expect(response.status).toBe(200)
expect(response.headers.get("content-type")).toBe("text/event-stream")
expect(await response.text()).toContain("datastar-patch-elements")

See examples/hono-todos for a complete app with tests that cover the initial HTML page, signal validation errors, element patches, and ordinary 404 responses.

What to test

LayerGood coverage
Pure helpersDatastar attributes, expression rendering, HTML escaping, prop merging.
Request/response handlersread.signals, auth and validation behavior, reply.* status and headers.
Protocol helpersSSE encoding, patch options, signal patch options, stream chunks.
ExamplesRuntime wiring and copyable application patterns.
Browser behaviorAssumptions that require the real Datastar browser runtime.

Use browser/runtime tests for behavior that unit tests cannot prove, such as how Datastar applies a patch in the DOM.

Keep examples honest

When changing examples, keep their tests and checks close to the example package. An example should remain copyable and clear about its dependencies.

Observability is app-owned. Test your logging, tracing, metrics, and OpenTelemetry setup through the platform libraries you use in production.

Next: Examples.