---
url: /blog/2026/v0.6.2.md
description: >-
  Vitest integration lands. A test file next to every route, backend tests
  through the real stack with no network, and frontend tests driving a real
  browser against a real server.
---

`0.6.2` is out, and the headline of the 0.6 line is Vitest.

Tests in a framework project are usually a separate concern:
their own toolchain, their own setup, their own mocks of everything the app touches.
Which is also why the third route in a project often ships without one.

I wanted a new route to arrive with a test next to it, the way it arrives with a handler.
So that is what happens now.

## Backend: the real stack, no mocks

A backend test runs your route through the real thing: routing, middleware, validation,
handler, error handling. No network, no running server, no mocks.

It was cheap to build, because the pieces were already there.
The isomorphic fetch client from July dispatches into the API in-process during SSR.
The harness points that same dispatch at the test runner instead.

```ts
import { describe, expect, test } from "vitest";

import { prepareHarness } from "_/test/api";

const { client, route } = await prepareHarness("account");

describe(route, () => {
  test(route, async () => {
    const { body, response } = await client.POST([], { json: { some: "data" } });
    expect(response.status).toEqual(200);
  });
});
```

One difference from the client you use in the app: the harness client never throws.
It hands back `{ body, response }`, because a test wants to assert on a `400` or a `401`,
not catch it.

## Frontend: a real browser, a real server

A frontend test starts a real server on a random port, so it never collides with your dev server,
and hands the test a URL. No DOM shim stands in for the page.

The browser is yours: Playwright, Puppeteer, whatever the project already uses.
The harness owns the server, not the client.

```ts
import { describe, test } from "vitest";

import { prepareHarness } from "_/test/pages";
import { browser } from "./browser";

const { page, route } = await prepareHarness("account");

describe(route, () => {
  test(route, async () => {
    const tab = await browser.newPage();
    await tab.goto(page.href());
  });
});
```

## Seeded, like everything else

Every route gets a starter `index.test.ts` beside it, in `api/` or `pages/`,
seeded the same way routes are.

Testing is enabled by default. Set `test: false` to opt out of it entirely,
or `test: { seed: false }` to keep the harness and write the files yourself.

Running them is what you would expect, and Vitest path filters narrow a run
to a source folder, one side of it, or a single route:

```sh
pnpm test
pnpm test <folder>/api
pnpm test <folder>/pages/<route>
```

Source folders with testing enabled are loaded into your `vitest.config.ts` through `loadConfig()`.
The file is yours to extend, just keep that call in place.

More in the [backend](/backend/testing) and [frontend](/frontend/testing) testing docs.

## Also in 0.6

* `devPort` and `previewPort` are replaced by `kosmo.devHost` and `kosmo.previewHost` in `package.json`
* route path helpers no longer ask for a params argument on routes that have no params
* finding a free port range got more robust

Tests are no longer a separate concern. They are files in the folder, seeded like routes,
and asserted against the real app.
