From 639e43685403a0fffc19c53eb9eccefe4b977b74 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 18 Mar 2026 17:12:36 -0400 Subject: [PATCH] docs: update tests/README and GUIDE__Development with current test patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/README.md: - Add shared helpers table (_helpers/ files and purpose) - Update "Writing / modifying tests" to reference setup_badge_test_page; badge tests are now the canonical template - Add "Hard-Won Lessons — Badge Print / IDB Tests" section covering: - __version guard: why ae_defaults.ts must include __version: 1 or store_versions.ts silently wipes ae_loc and loses trusted_access - IDB inject-then-reload pattern: why reload is required (liveQuery won't fire on raw IDB writes that bypass Dexie's notification system) - Fix pre-existing lint warnings: bare URLs → code spans, trailing newline GUIDE__Development.md: - Add Required Check #5: run Playwright integration tests for auth/store or badge print changes; badge tests are the canonical template - Add tests/README.md to Key Documentation table Co-Authored-By: Claude Sonnet 4.6 --- documentation/GUIDE__Development.md | 6 +++ tests/README.md | 80 +++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/documentation/GUIDE__Development.md b/documentation/GUIDE__Development.md index 662c4541..78b91dec 100644 --- a/documentation/GUIDE__Development.md +++ b/documentation/GUIDE__Development.md @@ -11,6 +11,11 @@ 2. **Type Safety:** Ensure interfaces in `src/lib/types/ae_types.ts` match backend schemas. 3. **Reactivity Check:** Verify Svelte 5 runes (`$state`, `$derived`) are not creating race conditions with Dexie `liveQuery`. 4. **Build Check:** For major changes, run `npm run build:staging` to ensure no SSR or build-time failures. +5. **Integration Tests:** For changes to badge print, event layouts, or auth/store logic, run the relevant Playwright test file(s): + ```bash + npx playwright test tests/event_badge_render.test.ts tests/event_badge_attendee_workflow.test.ts + ``` + Run the full suite with `npm run test:integration`. The badge tests (`event_badge_*.test.ts`) are the canonical integration test template. ## 2. Commit Policy - **Atomic Commits:** One component or one logic fix per commit. Do not batch unrelated changes. @@ -46,6 +51,7 @@ You are not working in a vacuum. Coordinate with the Backend Agent via MCP tools | `documentation/AE__Architecture.md` | System architecture overview | | `documentation/AE__Naming_Conventions.md` | Naming rules | | `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher reference | +| `tests/README.md` | Playwright test guide — shared helpers, hard-won lessons, demo IDs | ## 6. URL Parameters diff --git a/tests/README.md b/tests/README.md index f9e2c79f..cf80e239 100644 --- a/tests/README.md +++ b/tests/README.md @@ -25,20 +25,36 @@ Notes - Tests in `tests/disabled/` are ignored by default (see `playwright.config.ts`). Move flaky or environment-dependent tests there. - The runner starts a local dev server via `npm run dev` by default (see `playwright.config.ts:webServer`). Ensure the app can start on port `5173` or update the config. +Shared test helpers (`tests/_helpers/`) + +| File | Purpose | +| --- | --- | +| `env.ts` | Constants: `testing_event_id`, `testing_account_id`, `mock_site_domain` | +| `ae_defaults.ts` | `ae_app_local_data_defaults` — full localStorage seed object with `__version` | +| `idb_helpers.ts` | `inject_badge_and_template()` — write badge + template records into IndexedDB | +| `minimal_v3_mocks.ts` | `attach_minimal_v3_routes()`, `seed_trusted_session()`, `setup_badge_test_page()` | + +**`setup_badge_test_page(page, event_id)`** is the one-call `beforeEach` for any badge/event print page test. It wires the pageerror listener, all V3 API mocks, and the trusted auth localStorage seed in one call. + Writing / modifying tests -- Tests are TypeScript files under `tests/` and should export Playwright `test` blocks. Example header: +- Tests are TypeScript files under `tests/` and should export Playwright `test` blocks. +- The badge tests (`event_badge_*.test.ts`) are the **canonical template** — copy the pattern from there when adding tests for any new event module feature. +- Minimal `beforeEach` using shared helpers: ```ts -import { test, expect } from '@playwright/test'; +import { testing_event_id } from './_helpers/env'; +import { inject_badge_and_template } from './_helpers/idb_helpers'; // only if IDB needed +import { setup_badge_test_page } from './_helpers/minimal_v3_mocks'; -test('example', async ({ page }) => { - await page.goto('/'); - await expect(page).toHaveTitle(/OSIT/); +const event_id = testing_event_id; + +test.beforeEach(async ({ page }) => { + await setup_badge_test_page(page, event_id); }); ``` -- Use `page.route('**/v3/**', handler)` to mock backend responses for deterministic tests. -- Use `page.addInitScript` to inject `ae_loc` localStorage defaults when tests need authenticated/admin state. +- If you need IDB data (badge, template), use the inject-then-reload pattern (see Hard-Won Lessons below). +- Use `page.route('**/v3/**', handler)` to mock backend responses. `attach_minimal_v3_routes` covers the common cases; add inline `page.route()` calls only for test-specific overrides. Adding new tests - Create a new file `tests/my_feature.test.ts`. @@ -58,6 +74,50 @@ Help --- +## Hard-Won Lessons — Badge Print / IDB Tests + +These lessons came from debugging the badge attendee workflow tests. Document them here so the next person doesn't spend hours on the same issues. + +### The `__version` Guard — Always Include It in `ae_app_local_data_defaults` + +**Symptom:** Tests set `trusted_access: true` in `addInitScript`, but after `page.reload()` the print button is gone. Logging `$ae_loc.trusted_access` after reload shows `false`. + +**Cause:** `src/lib/stores/store_versions.ts` is a module-level side-effect import that runs *before* `persisted()` hydrates the store. It reads `ae_loc` from localStorage and calls `localStorage.removeItem('ae_loc')` if `parsed.__version !== AE_LOC_VERSION`. After the wipe, `persisted()` falls back to its `initialValue` (app defaults where `trusted_access: false`), erasing the test's auth seed. + +**Fix:** `ae_defaults.ts` must include `__version: 1` (matching `AE_LOC_VERSION` in `store_versions.ts`). This is already done — **do not remove it**. + +```typescript +export const ae_app_local_data_defaults = { + __version: 1, // Must match AE_LOC_VERSION in store_versions.ts — store_versions.ts + // wipes ae_loc if version doesn't match. Tests will silently lose auth. + ... +}; +``` + +If `AE_LOC_VERSION` ever increments in `store_versions.ts`, update the value here too or every test that relies on `trusted_access` will silently break. + +--- + +### The IDB Inject-Then-Reload Pattern + +**Why reload?** Dexie's `liveQuery` subscribes to IDB change notifications. Writing directly to IDB via the raw `indexedDB` API (as `inject_badge_and_template` does) bypasses Dexie's notification system — `liveQuery` will not fire. Reloading the page forces Dexie to open fresh, query the now-populated IDB, and fire `liveQuery` with the seeded data. + +**Correct pattern:** +```typescript +await page.goto(`/events/${event_id}/badges/${badge_id}/print`); +await page.waitForLoadState('domcontentloaded'); +// First nav initializes the Dexie schema. Now inject data. +await page.evaluate(inject_badge_and_template, { badge, template }); +// Reload so liveQuery starts fresh against populated IDB. +await page.reload(); +await page.waitForLoadState('domcontentloaded'); +await page.waitForSelector('.event_badge_wrapper', { timeout: 8000 }); +``` + +**Do not** try to `waitForFunction` for IDB changes after `inject_badge_and_template` without reloading — it will time out because liveQuery will not re-fire. + +--- + ## Deep Dive: Testing IDAA Edit Form Components This section documents hard-won lessons from writing `idaa_recovery_meeting_edit.test.ts`. These issues are not obvious and cost significant debugging time — read this before writing tests for any Svelte 5 form component. @@ -231,8 +291,8 @@ await page.route(`**/v3/crud/event/${event_id}`, async (route) => { ## Development / Testing / Demo environment information * Use snake_case (or Snake_Case or Snake_case or test_NASA_example or test_API_key) -* Aether test/demo base URL: 'http://demo.localhost:5173' -* Aether development API: 'https://dev-api.oneskyit.com' +* Aether test/demo base URL: `http://demo.localhost:5173` +* Aether development API: `https://dev-api.oneskyit.com` These are IDs for records that we can use for testing. Please do not delete them. They are also used for demo purposes with clients. @@ -276,4 +336,4 @@ These are IDs for records that we can use for testing. Please do not delete them ### Events Module (IDAA Recovery Meetings) * Aether test/demo Event: '1Pkd025vvxU' (36) "IDAA Recovery Meeting Test" -* Aether test/demo Event: 'gIZgAjISkf8' (43) "IDAA Recovery Meeting Test" \ No newline at end of file +* Aether test/demo Event: 'gIZgAjISkf8' (43) "IDAA Recovery Meeting Test"