# Learnings — cts-herd-playwright ## Inherited from Phase 1 (cts-presenter-app) ### Vue Key Pattern For repeating groups in arrangements, MUST use `${group.id}-${index}` NOT just `group.id` ### PDF Generation Old-school CSS only (NO Tailwind) with DejaVu Sans font for German umlauts ### Auto-Save 500ms debounce for text, immediate for selects/checkboxes via `useDebounceFn` ### Line-Count Translation Distribute translated text by matching original slide line counts ### SQLite date gotcha Returns `YYYY-MM-DD 00:00:00` instead of `YYYY-MM-DD` — needs `substr($date, 0, 10)` --- ## Phase 2 Specific ## [2026-03-01 23:10] Wave 2-3 Completion Session ### Playwright Test Patterns Established **Auth Setup Pattern** (auth.setup.ts): - Navigate to /login to establish session cookies (XSRF-TOKEN) - Extract XSRF token from cookies: `decodeURIComponent(xsrfCookie.value)` - POST to /dev-login with XSRF token in headers - Navigate to /dashboard to confirm login - Save storageState to tests/e2e/.auth/user.json - Pattern works reliably for all authenticated tests **Test Structure Pattern**: ```typescript test('description', async ({ page }) => { await page.goto('/url'); await page.waitForLoadState('networkidle'); // CRITICAL for Inertia apps await expect(page).toHaveURL(/pattern/); await expect(page.getByTestId('testid')).toBeVisible(); }); ``` **CSRF Protection Pattern** (for POST requests in tests): ```typescript const cookies = await page.context().cookies(); const xsrfCookie = cookies.find((c) => c.name === 'XSRF-TOKEN'); const xsrfToken = xsrfCookie ? decodeURIComponent(xsrfCookie.value) : ''; await page.request.post('/endpoint', { headers: { 'X-XSRF-TOKEN': xsrfToken } }); ``` ### Session Timeout Handling **Issue**: Long-running task() calls timeout after 10 minutes (600000ms) **Solution**: 1. Check if file was created despite timeout: `ls -la tests/e2e/{filename}.spec.ts` 2. If created, verify tests: `npx playwright test {filename}.spec.ts` 3. If tests pass, proceed with verification and commit 4. If tests fail, resume session with session_id (saves 70%+ tokens) **Pattern**: Timeouts don't mean failure — check actual output before retrying ### data-testid Naming Conventions **Established Patterns**: - Navigation: `auth-layout-nav-{page}` (e.g., `auth-layout-nav-services`) - User controls: `auth-layout-user-dropdown-trigger`, `auth-layout-logout-link` - Sync: `auth-layout-sync-button`, `auth-layout-sync-timestamp` - Lists: `{feature}-list-table`, `{feature}-list-row-{id}`, `{feature}-list-empty` - Actions: `{feature}-list-{action}-button` (e.g., `service-list-edit-button`) - Blocks: `{block}-block-{element}-{id}` (e.g., `information-block-thumbnail-{id}`) **Rule**: Always use kebab-case, always include component context, always be specific ### German UI Text Assertions **Common Terms**: - Navigation: "Gottesdienste", "Song-Datenbank" - Actions: "Bearbeiten", "Finalisieren", "Wieder öffnen", "Herunterladen", "Löschen" - Auth: "Mit ChurchTools anmelden", "Abmelden", "Test-Anmeldung" - General: "Willkommen", "Ablaufdatum", "Vorschau", "Zuweisen", "Mit Übersetzung" **Rule**: Always use exact German text from Vue components, never English ### Inertia.js + Playwright Gotchas **Issue**: Inertia apps render client-side, so page.goto() returns before Vue renders **Solution**: ALWAYS use `await page.waitForLoadState('networkidle')` after navigation **Issue**: data-testid attributes don't appear in raw HTML (curl output) **Solution**: Check compiled JS bundles: `grep -r 'data-testid' public/build/assets/*.js` ### Parallel Task Execution **Wave 3 Pattern**: All 6 tasks (T8-T13) can run in parallel - Each creates independent spec file - No shared state between tests - All use same storageState (auth.setup.ts) - workers:1 in playwright.config.ts prevents SQLite conflicts **Optimization**: Dispatch all 6 tasks in ONE message for maximum parallelism ### Verification Best Practices **4-Phase Verification** (MANDATORY): 1. **Read Code**: Read EVERY changed file line-by-line 2. **Automated Checks**: Run tests, build, lsp_diagnostics 3. **Hands-On QA**: Actually run the tests and see them pass 4. **Gate Decision**: Can explain every line? Saw it work? Confident nothing broken? **Evidence Files**: Save test output to `.sisyphus/evidence/task-{number}-{name}.txt` **Commit Messages**: Use conventional commits format: ``` test(e2e): add {feature} E2E tests - X tests: {list} - German UI text assertions - All tests passing ``` ### Token Budget Management **Session Stats**: - Started: 200K tokens - Used: ~124K tokens (62%) - Remaining: ~76K tokens (38%) - Tasks completed: 7/24 (29.2%) **Optimization**: Use session_id for retries (saves 70%+ tokens vs new task) **Strategy**: Focus on completing Wave 3 (6 tasks) before token exhaustion