diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f4632a..b316a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - No server or OpenAPI changes — both surfaces reuse the existing fly endpoint (validations unchanged: 404 unknown plane / 409 flying or insufficient fuel / 400 unknown ICAO, strict unknown-field rejection kept) - Tests: e2e — new `e2e/relocation.spec.ts` (serial mode, SCALE=1000, self-heal fly-home in afterEach like actions.spec.ts): market-row test ensures plane-starter grounded at the hub via API, picks it + the nearest non-hub airport (shortest leg) and asserts `/api/state` shows `flying` with that destIcao after clicking Relocate; popup test clicks the isolated LFRS marker (projected-center pattern from map.spec.ts), asserts the select lists "ATR 72-600 — EDDF" and the button is disabled until a plane is picked, then relocates via the popup. Marker-size regression gate = the existing map.spec.ts click tests keep passing - Verified: `tsc --noEmit` clean; unit suite green locally (no lib/ changes — flyPlane's zero-package case already covered by actions.test.ts); e2e not run locally by design — CI verifies against an isolated stack +- **Fix (CI run #50): POPUP_AIRPORT scoping in the popup relocation e2e.** The LFRS marker click passed `POPUP_AIRPORT` into a `page.evaluate(() => …)` callback that runs in the BROWSER, where Node-scope constants don't exist — deterministic `ReferenceError: POPUP_AIRPORT is not defined` (relocation.spec.ts popup test, both attempts of run #50). The constant is now passed as an evaluate argument (`page.evaluate(({lon, lat}) => …, POPUP_AIRPORT)`); the other evaluate in the file only touches browser globals and needed no change. Test-only — no app code touched ## v0.17.0 — 2026-09-19 14:49 - **Bottom-right toasts for action errors (ISS-008).** Error messages like "insufficient funds: a ATR 72-600 costs 200000, you have 62721" now appear as toasts in the bottom-right corner — new `lib/useToasts.ts` hook (`useToasts() → {toasts, push(kind, msg), dismiss(id)}`, auto-dismiss after ~5 s, queue capped at 4 dropping the oldest; pure core `pushToast` unit-tested) and new `components/Toasts.tsx` `` (fixed container `.toasts`, items `[data-testid="toast"]` with class `toast toast-error|toast-info` + a × dismiss button). Wired into BOTH error paths: `RightPane.runAction`'s catch now calls the shell's `onError` in addition to keeping the inline `[data-testid="right-error"]` pane error, and `LeftPane.applySimControls`'s catch (previously console.error-only) reports "Failed to update time controls (…)" through the same callback. The shell (`app/page.tsx`) owns the hook and renders the stack; CSS in `app/globals.css` diff --git a/e2e/relocation.spec.ts b/e2e/relocation.spec.ts index 17414f4..d69e798 100644 --- a/e2e/relocation.spec.ts +++ b/e2e/relocation.spec.ts @@ -151,13 +151,14 @@ test('airport popup offers Relocate options for grounded planes', async ({ page { timeout: 15_000 }, ).toBe(true); - // Click the LFRS marker (projected center — same pattern as e2e/map.spec.ts). - const pt = await page.evaluate(() => { + // Click the LFRS marker (projected center — same pattern as e2e/map.spec.ts). The constant is + // passed AS AN ARGUMENT: the evaluate callback runs in the browser, where Node-scope values don't exist. + const pt = await page.evaluate(({ lon, lat }) => { const map = (window as any).__flightMap; - const p = map.project([POPUP_AIRPORT.lon, POPUP_AIRPORT.lat]); + const p = map.project([lon, lat]); const rect = map.getContainer().getBoundingClientRect(); return { x: rect.left + p.x, y: rect.top + p.y }; - }); + }, POPUP_AIRPORT); await page.mouse.click(pt.x, pt.y); const popup = page.getByTestId('airport-popup');