testing
Test components and API routes with everything already wired up.
The hard part of testing a component is rarely the test. It's setting up the providers, the query cache and the router it expects. kitr/testing renders components inside your real app, and sends requests to your API without starting a server.
Install
kitr add testingFirst time? Run npm i -g kitr once. Inside a project, the global kitr runs your project's own version.
This installs Vitest, plus Testing Library and happy-dom when the project has a UI. It writes vitest.config.ts and an example test. Run the tests with:
pnpm test
Tests live next to the code (app/**/*.test.tsx) or under test/.
Components
// app/features/projects/project-list.test.tsx
import { screen } from '@testing-library/react'
import { createTestApp } from 'kitr/testing'
import { renderWithKitr } from 'kitr/testing/react'
import { config } from 'virtual:kitr/config'
import { providers } from 'virtual:kitr/providers'
import { expect, it } from 'vitest'
import { ProjectList } from './project-list'
it('lists projects', async () => {
const app = await createTestApp({ providers, config })
renderWithKitr(<ProjectList />, {
app,
queryData: [[['projects', 'list'], [{ id: '1', name: 'Alpha' }]]],
})
expect(await screen.findByText('Alpha')).toBeTruthy()
})
createTestApp boots an app with environment: 'test' and whatever providers and config you pass. The virtual:kitr/* modules give you the same ones your app uses; the kitr() plugin in vitest.config.ts provides them. renderWithKitr wraps the component in that app's providers, and queryData fills the query cache first, so the component renders data without any network.
To test a component that uses links or route hooks, pass a wrapper that adds a router with a memory history.
API routes
// test/projects.test.ts
import { createTestClient } from 'kitr/testing'
import { expect, it } from 'vitest'
import { server } from '../server/main'
it('creates a project', async () => {
const client = createTestClient(server)
const res = await client.post<{ id: string }>('/api/projects', { name: 'Alpha' })
expect(res.status).toBeLessThan(300)
expect(res.body.id).toBeTruthy()
})
it('rejects a short name', async () => {
const res = await createTestClient(server).post('/api/projects', { name: 'A' })
expect(res.status).toBe(422)
})
The client calls server.fetch() directly, so no port is opened. It also accepts any object with a fetch(request) method, or a URL string to test a running deployment. JSON bodies are sent and parsed for you, and res.response holds the raw Response.
Pass default headers once, for example to sign every request in:
const client = createTestClient(server, { headers: { authorization: `Bearer ${token}` } })
Faking the API
To test the frontend against an API that doesn't exist yet, or to force an error case, use mocks.