🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started →
Documentation
Guides
Testing

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: true

See 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/test

Playwright 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/testing

Configure 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

ExportDescription
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 testID on all interactive elements for stable selectors
  • Keep business logic separate from components for easy unit testing
  • Use useLiveQuery for reactive data — it makes tests predictable without mocks
  • Use @velox/testing for component tests — it stubs all native APIs so tests run in Node