wwwwwwwwwwwwwwwwwww

Testing

Integration and unit testing with Playwright and Vitest

Takeout includes integration tests (Playwright) and unit tests (Vitest). We’ve found this combination covers what matters without the overhead of native integration tests, which tend to be a net loss unless you’re on a large project with dedicated QA resources.

Running Tests

The easiest way to run the full test suite:

bun run ci release --dry-run # full pipeline without deploying
bun run ci release --dry-run --dev # faster: uses dev server instead of build

This starts backend, runs unit tests, builds/serves frontend, and runs integration tests automatically.

For running tests individually:

bun test:unit # unit tests only (no backend needed)
bun test:integration # integration tests only (needs backend + frontend running)

For manual integration test setup:

# terminal 1: start backend (optional: backend:clean first for fresh db)
bun backend
# terminal 2: start frontend
bun dev
# terminal 3: run tests
bun test:integration

Integration Tests

Integration tests live in src/test/integration/ and use Playwright. They test real user flows—login, creating posts, file uploads, API endpoints.

// src/test/integration/post-flow.test.ts
import { test, expect } from '@playwright/test'
import { loginAsDemo } from './helpers'
test('can create a post', async ({ page }) => {
await loginAsDemo(page, '/')
await page.click('[data-testid="new-post"]')
await page.fill('[data-testid="post-content"]', 'Hello world')
await page.click('[data-testid="submit"]')
await expect(page.locator('text="Hello world"')).toBeVisible()
})

Helpers in helpers.ts handle common operations like loginAsDemo(), loginAsAdmin(), and waitForZeroSync().

Database State

For a clean database, use bun backend:clean && bun backend. Tests create data that persists, and stale data/keys can cause flaky failures or auth errors.

Debugging

PWDEBUG=1 bun test:integration --headed # opens inspector

Traces are saved on failure—view them with:

bunx playwright show-trace src/test/integration/.output/test-results/*/trace.zip

Unit Tests

Unit tests live in src/test/unit/ and packages/*/src/. They test isolated logic—utilities, hooks, data transformations.

// packages/hooks/src/useDeepMemoizedObject.test.ts
import { describe, test, expect } from 'vitest'
describe('useDeepMemoizedObject', () => {
test('preserves reference for equal objects', () => {
// ...
})
})

Run a specific test file:

bun test:unit packages/hooks/src/useDeepMemoizedObject.test.ts

CI Integration

Tests run automatically in CI via bun run ci release. The full pipeline:

bun run ci release --dry-run # run locally without deploying

CI uses single-worker mode with one retry for consistency.

Why No Native Tests

React Native integration tests require simulators, are slow to run, flaky across environments, and expensive to maintain. Unless you have a large team with QA resources, the ROI is negative, especially when you’re starting out.

Web integration tests catch many issues as you share most code between web and native. For native-specific behavior, manual testing or lightweight snapshot tests are more practical.

We include an Xcode MCP so agents can effectively smoke test your app while building features.

We’d like to include a simple test setup eventually. We have robust ones in the Tamagui and One repos you can reference.

Edit this page on GitHub.