# E2E CLAUDE.md Playwright end-to-end tests. ## Commands ```bash npm test # Headless npm run test:headed # Visible browser npm run test:ui # Interactive UI npx playwright test tests/basic.spec.ts # Specific file npx playwright test -g "test name" # By name ``` ## Test Ports | Service | Dev Port | E2E Port | |---------|----------|----------| | Backend | 3000 | 3001 | | Frontend | 5173 | 5174 | | Database | spiceflow.db | test-e2e.db | ## Directory Structure ``` e2e/ ├── tests/ # Test files ├── global-setup.ts # Starts servers ├── global-teardown.ts # Stops servers ├── server-utils.ts # Server utilities └── playwright.config.ts # Configuration ``` ## Test Pattern ```typescript import { test, expect } from '@playwright/test'; import { E2E_BACKEND_URL } from '../playwright.config'; test('example', async ({ page, request }) => { // API setup const res = await request.post(`${E2E_BACKEND_URL}/api/sessions`, { data: { provider: 'claude' } }); const session = await res.json(); // Browser interaction await page.goto(`/session/${session.id}`); await page.fill('textarea', 'Hello'); await page.click('button:has-text("Send")'); // Assertions await expect(page.locator('.assistant-message')).toBeVisible({ timeout: 30000 }); // Cleanup await request.delete(`${E2E_BACKEND_URL}/api/sessions/${session.id}`); }); ``` ## Selectors ```typescript page.locator('[data-testid="x"]') // By test ID page.getByRole('button', { name: 'Send' }) page.locator('text=Hello') page.locator('.class') ``` ## Waits ```typescript await expect(locator).toBeVisible(); await expect(locator).toBeVisible({ timeout: 30000 }); await expect(locator).toHaveText('Expected'); await expect(locator).not.toBeVisible(); ``` ## Test Files | File | Tests | |------|-------| | `basic.spec.ts` | Health, loading | | `workflow-claude.spec.ts` | Claude session flow | | `permissions-claude.spec.ts` | Permission handling | | `autoaccept-claude.spec.ts` | Auto-accept edits | | `tmux-terminal.spec.ts` | Tmux sessions |