Testing
VeloxKit apps are standard React apps, so you can use any testing framework you already know. Components render with a native element tree that supports testID for reliable selection in tests.
Unit testing with Vitest
npm install -D vitest// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.ts'],
},
})Write a test:
// src/utils/format.test.ts
import { describe, it, expect } from 'vitest'
import { formatDate } from './format'
describe('formatDate', () => {
it('formats a timestamp', () => {
const result = formatDate(1717000000)
expect(result).toBe('May 29, 2024')
})
})Component testing
Use @testing-library/react to render VeloxKit components. The native renderer runs in Node — no DOM, no JSDOM, no browser required.
npm install -D @testing-library/react// src/components/NoteCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import { NoteCard } from './NoteCard'
describe('NoteCard', () => {
it('renders the note title', () => {
render(<NoteCard title="Hello" body="World" />)
expect(screen.getByText('Hello')).toBeTruthy()
})
it('calls onPress when clicked', () => {
const onPress = vi.fn()
render(<NoteCard title="Hello" onPress={onPress} />)
fireEvent.press(screen.getByTestId('note-card'))
expect(onPress).toHaveBeenCalledOnce()
})
})testID
Every VeloxKit component accepts a testID prop, which acts as a stable selector for tests. Use it instead of relying on text content or index.
<Pressable testID="note-card" onPress={handlePress}>
<Text>Hello</Text>
</Pressable>screen.getByTestId('note-card')Running tests in CI
Add a test step to your CI pipeline:
- run: npm ci
- run: npx vitest run
env:
CI: trueSee the packaging guide for a full CI/CD example.
E2E testing
For end-to-end tests, use a test runner that spawns the VeloxKit app and drives it via accessibility or CDP.
npm install -D @playwright/testPlaywright can connect to the VeloxKit CDP inspector (see Debugging) to inspect the element tree and run assertions.
import { test, expect } from '@playwright/test'
test('creates a new note', async ({ page }) => {
await page.goto('http://localhost:9229')
await page.click('[data-testid="new-note-button"]')
await page.fill('[data-testid="note-title"]', 'My Note')
await expect(page.locator('[data-testid="note-card"]')).toHaveCount(1)
})E2E testing requires setting inspect: true in dev mode. This is only available in development builds.
Testing with @velox/testing
@velox/testing is VeloxKit's dedicated testing package. It stubs all native __velox_* bindings so your components can run in Node without a native window.
npm install -D @velox/testingConfigure Bun to auto-load the stubs:
# bunfig.toml
[test]
preload = ["@velox/testing/setup"]Or install manually in each test:
import { installStubs } from '@velox/testing'
installStubs()render / screen
import { render, screen, fireEvent, waitFor } from '@velox/testing'
import { NoteCard } from './NoteCard'
test('renders note title', () => {
render(<NoteCard title="Hello" body="World" />)
expect(screen.getByText('Hello')).toBeTruthy()
})
test('calls onPress when tapped', async () => {
const onPress = jest.fn()
render(<NoteCard title="Hello" onPress={onPress} />)
fireEvent.press(screen.getByText('Hello'))
expect(onPress).toHaveBeenCalledOnce()
})Mocking native bindings
import { mockBinding } from '@velox/testing'
mockBinding('__velox_fs_read_text_file', async () => 'file contents')
mockBinding('__velox_db_query', async () => JSON.stringify([{ id: 1, title: 'Note' }]))act / waitFor
import { render, screen, act, waitFor } from '@velox/testing'
test('loads notes', async () => {
const { rerender } = render(<NotesList />)
await waitFor(() => {
expect(screen.getByText('My Note')).toBeTruthy()
})
})API
| Export | Description |
|---|---|
installStubs() | Mock all __velox_* native bindings |
render(element) | Render a component tree (react-dom/server SSR) |
screen.getByText(text) | Find element by text content |
screen.queryByText(text) | Like getByText, returns null if not found |
screen.getAllByText(text) | Find all matching elements |
screen.debug() | Print the rendered tree |
act(callback) | Wrap async state updates |
fireEvent.press(element) | Simulate a press |
fireEvent.changeText(element, text) | Simulate text input |
fireEvent.submitEditing(element) | Simulate form submit |
waitFor(assertion, opts?) | Poll until assertion passes |
mockBinding(name, fn) | Replace a specific __velox_* binding |
See the packages/@velox/testing reference for the full API.
Best practices
- Use
testIDon all interactive elements for stable selectors - Keep business logic separate from components for easy unit testing
- Use
useLiveQueryfor reactive data — it makes tests predictable without mocks - Use
@velox/testingfor component tests — it stubs all native APIs so tests run in Node