docs: update tests/README and GUIDE__Development with current test patterns

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 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-18 17:12:36 -04:00
parent 81741919a8
commit 639e436854
2 changed files with 76 additions and 10 deletions

View File

@@ -11,6 +11,11 @@
2. **Type Safety:** Ensure interfaces in `src/lib/types/ae_types.ts` match backend schemas. 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`. 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. 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 ## 2. Commit Policy
- **Atomic Commits:** One component or one logic fix per commit. Do not batch unrelated changes. - **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__Architecture.md` | System architecture overview |
| `documentation/AE__Naming_Conventions.md` | Naming rules | | `documentation/AE__Naming_Conventions.md` | Naming rules |
| `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher reference | | `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 ## 6. URL Parameters

View File

@@ -25,20 +25,36 @@ Notes
- Tests in `tests/disabled/` are ignored by default (see `playwright.config.ts`). Move flaky or environment-dependent tests there. - 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. - 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 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 ```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 }) => { const event_id = testing_event_id;
await page.goto('/');
await expect(page).toHaveTitle(/OSIT/); test.beforeEach(async ({ page }) => {
await setup_badge_test_page(page, event_id);
}); });
``` ```
- Use `page.route('**/v3/**', handler)` to mock backend responses for deterministic tests. - If you need IDB data (badge, template), use the inject-then-reload pattern (see Hard-Won Lessons below).
- Use `page.addInitScript` to inject `ae_loc` localStorage defaults when tests need authenticated/admin state. - 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 Adding new tests
- Create a new file `tests/my_feature.test.ts`. - 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 ## 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. 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 ## Development / Testing / Demo environment information
* Use snake_case (or Snake_Case or Snake_case or test_NASA_example or test_API_key) * 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 test/demo base URL: `http://demo.localhost:5173`
* Aether development API: 'https://dev-api.oneskyit.com' * 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. 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.