@velox/testing Stable
Unit testing utilities for VeloxKit apps. Works with Bun's built-in test runner (opens in a new tab) (bun test).
What it does:
- Mocks all
__velox_*native bindings so React components that call Velox APIs run in a plain Bun process — no GPU, no winit, no running window required. - Renders components to a string via
react-dom/server(SSR-style) for headless query. - Provides
screen,fireEvent,act, andwaitForhelpers in a style familiar to@testing-library/reactusers.
Install
bun add -d @velox/testingSetup
Add the setup preload to bunfig.toml so stubs are installed before every test file:
# bunfig.toml
[test]
preload = ["@velox/testing/setup"]The setup script calls installStubs() once globally. You can also call it manually in beforeEach for finer control.
API reference
installStubs()
Installs sensible no-op stubs for every __velox_* native binding on globalThis.
Must be called before importing any VeloxKit component under test.
import { installStubs } from '@velox/testing'
beforeAll(() => installStubs())Stubbed bindings include: scene graph, window, file system, database, network, credentials, clipboard, dialog, notifications, audio, canvas, AI, camera, crash, perf, power, system, storage, deep link, HID, mDNS, WebSocket, vector DB, and backend command dispatch.
render(element)
Render a React element and return query helpers. Uses react-dom/server for
server-side markup rendering — no DOM or native window required.
render(element: ReactElement): Promise<{
container: string
getByText: (text: string) => Node
queryByText: (text: string) => Node | null
getAllByText: (text: string) => Node[]
debug: () => void
}>import { render } from '@velox/testing'
import { describe, test, expect } from 'bun:test'
import Counter from './Counter'
test('renders initial count', async () => {
const { getByText } = await render(<Counter initialValue={0} />)
expect(getByText('0')).toBeTruthy()
})screen
Shorthand query object for the most recently rendered element. Mirrors
@testing-library/react's screen export.
screen.getByText(text: string): Node // throws if not found
screen.queryByText(text: string): Node | null // returns null if not found
screen.getAllByText(text: string): Node[] // throws if none found
screen.debug(): void // prints rendered HTMLimport { render, screen } from '@velox/testing'
test('shows error message', async () => {
await render(<LoginForm />)
screen.debug()
expect(screen.queryByText('Invalid password')).toBeNull()
})act(callback)
Wraps state updates and async operations so React can flush them before assertions.
act(callback: () => void | Promise<void>): Promise<void>import { render, act, screen } from '@velox/testing'
test('toggles visibility', async () => {
const { getByText } = await render(<ToggleDemo />)
const btn = getByText('Show')
await act(() => {
btn.onPress?.()
})
expect(screen.getByText('Hidden content')).toBeTruthy()
})fireEvent
Simulate user interactions on nodes returned by getByText.
fireEvent.press(node) // triggers onPress
fireEvent.changeText(node, text) // triggers onChangeText / onChange
fireEvent.submitEditing(node) // triggers onSubmitEditingimport { render, fireEvent, screen } from '@velox/testing'
test('TextInput fires onChange', async () => {
const onChange = jest.fn()
const { getByText } = await render(
<TextInput placeholder="Name" onChangeText={onChange} />
)
const input = getByText('Name')
fireEvent.changeText(input, 'Alice')
expect(onChange).toHaveBeenCalledWith('Alice')
})waitFor(assertion, opts?)
Poll an assertion until it passes or timeout expires. Useful for async state updates.
waitFor(
assertion: () => void,
opts?: { timeout?: number; interval?: number }
): Promise<void>import { render, waitFor, screen } from '@velox/testing'
test('loads data asynchronously', async () => {
await render(<DataLoader />)
await waitFor(() => {
expect(screen.getByText('Loaded!')).toBeTruthy()
}, { timeout: 2000 })
})mockBinding(name, impl)
Register a custom mock for a specific __velox_* binding, overriding the auto-stub.
mockBinding(name: string, impl: Function): voidimport { installStubs, mockBinding, render } from '@velox/testing'
beforeAll(() => {
installStubs()
// Override the fetch stub to return real fixture data
mockBinding('__velox_fetch', () =>
Promise.resolve(JSON.stringify({
status: 200,
ok: true,
body: JSON.stringify({ notes: [{ id: 1, title: 'Test note' }] }),
headers: { 'content-type': 'application/json' },
}))
)
})
test('displays fetched notes', async () => {
const { getByText } = await render(<NotesList />)
await waitFor(() => expect(getByText('Test note')).toBeTruthy())
})Full example
// tests/Counter.test.ts
import { describe, test, expect, beforeAll } from 'bun:test'
import { installStubs, render, fireEvent, screen, waitFor } from '@velox/testing'
import Counter from '../src/Counter'
beforeAll(() => installStubs())
describe('Counter', () => {
test('renders initial count', async () => {
const { getByText } = await render(<Counter initialValue={5} />)
expect(getByText('5')).toBeTruthy()
})
test('increments on press', async () => {
const { getByText } = await render(<Counter initialValue={0} />)
const plusBtn = getByText('+')
await act(() => fireEvent.press(plusBtn))
await waitFor(() => expect(screen.getByText('1')).toBeTruthy())
})
test('decrements on press', async () => {
const { getByText } = await render(<Counter initialValue={3} />)
await act(() => fireEvent.press(getByText('-')))
await waitFor(() => expect(screen.getByText('2')).toBeTruthy())
})
test('reset button returns to zero', async () => {
const { getByText } = await render(<Counter initialValue={10} />)
await act(() => fireEvent.press(getByText('Reset')))
await waitFor(() => expect(screen.getByText('0')).toBeTruthy())
})
})Run with:
bun testTesting components that use Velox APIs
Components that call Velox APIs (fetch, audio, credentials, etc.) work in tests
because installStubs() stubs every binding. Override individual stubs with
mockBinding() to control what data flows through your component:
// Test a component that saves to credentials
import { installStubs, mockBinding, render, fireEvent } from '@velox/testing'
beforeAll(() => {
installStubs()
const store = new Map()
mockBinding('__velox_credentials_set', (key, val) => {
store.set(key, val)
return Promise.resolve(null)
})
mockBinding('__velox_credentials_get', (key) => {
return Promise.resolve(store.get(key) ?? 'null')
})
})
test('saves API key', async () => {
const { getByText } = await render(<ApiKeyForm />)
fireEvent.changeText(getByText('API Key'), 'sk-abc123')
fireEvent.press(getByText('Save'))
await waitFor(() => expect(screen.getByText('Saved!')).toBeTruthy())
})@velox/testing is designed for unit and component tests. For end-to-end
testing of a fully running Velox window, see the Testing guide.