pp-planer/.sisyphus/notepads/cts-herd-playwright/learnings.md
Thorsten Bus 7c4eb31769 docs: add comprehensive continuation guide for CTS E2E testing
- Complete execution patterns for remaining 20 tasks
- 6-section prompt templates for each task type
- Troubleshooting guide for common issues
- Verification checklists and best practices
- Session learnings from Wave 2-3 completion
2026-03-01 23:17:11 +01:00

4.7 KiB

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:

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):

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