Every scaffolded app ships with Playwright tests inDocumentation Index
Fetch the complete documentation index at: https://docs.deep.space/llms.txt
Use this file to discover all available pages before exploring further.
tests/. The CLI’s test command bootstraps Playwright (downloads Chromium on first run), regenerates dev secrets, and runs the suite against the dev workers. Tests use real services - no mocking of internal hooks.
Three spec files
| File | Covers |
|---|---|
smoke.spec.ts | App boots, navigation renders, page titles, auth UI present |
api.spec.ts | API routes return expected shapes; auth gating; integration calls |
collab.spec.ts | Multi-user real-time sync - two users connect and see each other |
docs, kanban, messaging, …) does not add a new spec file. Extend these three.
Running tests
tests/playwright.config.ts starts Vite if it’s not already running and reuses it if it is.
Multi-user testing - the users fixture
The SDK ships a Playwright fixture from 'deepspace/testing' that returns N signed-in browser contexts:
MultiplayerUser is { context, page, email, name, userId? }. Contexts auto-close when the test finishes.
The fixture caches storageState per account, so each test account signs in once per machine - not once per test. This sidesteps Better Auth’s per-IP rate limit on /api/auth/sign-in/email and is materially faster as the suite grows.
Pick specific accounts by name:
Provisioning test accounts
The fixture reads from~/.deepspace/test-accounts.json, populated via the CLI:
@deepspace.test. Don’t bake the app name into the email - the same accounts work for every app.
Cap is 10 accounts per machine. Reuse what you have. If
collab.spec.ts ships with await users(['Collab A', 'Collab B']) and your pool doesn’t have those exact names, change the call to await users(2) to grab the first N accounts by createdAt regardless of name.The test extension checklist
Run tests only after a runtime-affecting code change (src/, worker.ts, etc.). Skip them for conversation, planning, or pure documentation edits.
| Trigger | Required test |
|---|---|
| Added a schema | smoke.spec.ts - CRUD happy path for a signed-in user |
| Added/edited a route, page, nav item, or top-level UI | smoke.spec.ts - page-load with real-content assertion |
Schema with visibilityField or 'public'/'shared'/'team'/'own' permissions | collab.spec.ts - two-user assertion (A acts, B sees) |
Used useYjs* / useMessages / useReactions / usePresence / useCanvas | collab.spec.ts - two-user assertion |
| Added/edited worker route, server action, AI chat, cron, integration call, or auth-gated UI | api.spec.ts - status codes + shape + auth gating |
| Fixing a bug | Write a failing test first, then fix. Leave the test in place. |
/api/integrations/<endpoint> and assert success: true with the data shape your UI consumes. This catches wrong endpoint names - the most common integration-heavy-app failure.
Test data cleanup
Tests run against the same local Durable Object the dev server uses, so anything you create persists. Two conventions to keep the dev DB clean:Prefix test records
Every record a test creates should start with
__test-${Date.now()}__ in its human-visible field (title, name, question).Auth-state assertions
The scaffold ships the mixed auth config. Every route falls into one of three buckets:| Route | Smoke assertion |
|---|---|
Public (src/pages/<name>.tsx) | Signed-out visitor sees real content; [data-testid="auth-overlay"] count is 0. |
Gated (src/pages/(protected)/<name>.tsx) | Signed-out: overlay visible and content not in DOM. Signed-in: content visible, no overlay. |
| After sign-out from gated | URL navigates to redirectOnSignOut (default /). Overlay does not appear. |
[data-testid="auth-overlay"] attribute is on the SDK’s <AuthOverlay/> - more reliable than text matching.
Route coverage
Every reachable route must have a test that:- Navigates to it (for dynamic routes, create a record first and use its ID)
- Waits for real content to appear (a specific element with real data - not just “no crash”)
- Fails loudly on empty/not-found states when there shouldn’t be one
Self-diagnosis with tests
When something isn’t working, don’t start with console logs. Start with:Write or tighten a test that expresses the expected behavior
Describe the assertion you’d run if the feature worked.
Screenshots for visual debugging
test. Use it for “what does this page actually render right now” workflows - not as a substitute for Playwright assertions.
Tips
- All tests use real services. No mocking of internal hooks. The whole point is exercising the real auth, storage, and integration pipelines end-to-end.
- Re-run after every follow-up change. Apply the extension checklist each turn - tests are a living contract.
- Don’t weaken tests to make them green. Write a more specific assertion, or fix the underlying behavior.
- Avoid
console.log-driven debugging. A tighter assertion gives better signal than a log ever will.
Next steps
- Testing reference -
usersfixture,loadAllTestAccounts,ensureStorageState. - CLI test command - flags and environment variables.