diff --git a/CHANGELOG.md b/CHANGELOG.md index b3bb448..9e9182f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - **Time-scale slider reworked to a 10-step ladder up to 1000× (ISS-006).** The old power-of-two ladder `1/2/4/8/16/32/64` is replaced by the discrete values `1, 2, 5, 10, 25, 50, 100, 250, 500, 1000` (each step roughly doubles the speed). The server clamp range widens from `[1, 64]` to `[1, 1000]` (`MAX_TIME_SCALE` in `lib/sim/engine.ts`, enforced by `clampTimeScale`; out-of-range values are still clamped, never rejected), and the OpenAPI spec's `timeScale` bounds + description follow. The slider mapping helpers in `lib/timeScale.ts` now place off-ladder scales at the nearest ladder step **by log distance** (the old `log2` formula assumed a pure power-of-two ladder); round-trip, monotonicity and endpoint-exactness are preserved - **E2e suite runs at the fastest time.** Every spec's fast-forward scale moves from 60× to the new top of 1000× (`sim`, `actions`, `right-pane`, `map-interactions`), so flights/expiries resolve in seconds instead of minutes. The map mid-flight test (`map-interactions.spec.ts`) needed a longer leg to keep its click assertions inside the flight window at 1000×: destination moves EDDV (Hannover, ~2 s real) → LEMD (Madrid–Barajas, ~10 s real), with a full-tank refuel before departure so the ~4970 L round trip always fits the 6300 L tank. The slider e2e (`panes.spec.ts`) now asserts End→`1000×` / Home→`1×`, and the clamp tests post `2000` → expect `1000` (500 is a valid ladder value, so it passes through unclamped) - Tests: unit — `lib/timeScale.test.ts` covers the 10-position ladder + log-distance nearest-step mapping; new `clampTimeScale` boundary suite in `lib/sim/engine.test.ts` (`[1, 1000]`, full ladder pass-through); `app/api/sim/route.test.ts` and `lib/openapi.test.ts` updated to the new bounds. Verified: unit suite **238/238** green locally (was 234 before this change) +- e2e `map-interactions.spec.ts`: the plane-popup test now pauses the sim around the marker click — at 1000× the rendered marker lags a fresh `/api/state` fetch by up to one map poll (~1 s), tens of px of drift on the EDDF→LEMD leg, so `page.mouse.click` missed the small circle layer (CI run #29); the sim is paused until the popup assertions pass, then resumed ## v0.13.1 — 2026-09-19 08:09 - CI e2e step now uses Playwright's list reporter so every test prints as it runs in the job log (progress visibility); failures still get full error context diff --git a/e2e/map-interactions.spec.ts b/e2e/map-interactions.spec.ts index d34abac..e323912 100644 --- a/e2e/map-interactions.spec.ts +++ b/e2e/map-interactions.spec.ts @@ -251,8 +251,23 @@ test('plane markers move while flying and the plane popup shows status + route', // The arc itself stays covered by unit tests: lib/mapData.test.ts.) // 4) Click the flying plane → status popup with model, status, fuel and the leg. - // Fetch + project in one evaluate so the click lands on a fresh position (at scale - // 1000 the marker moves fast — a stale projection would miss it). + // Freeze the sim so the click can't miss a fast-moving marker: at scale 1000 the rendered + // marker lags our fresh /api/state fetch by up to one map poll (~1 s) — tens of px of drift + // on this leg. Pausing makes the position static; we resume right after the popup checks. + await postSim(page, { paused: true }); + let prevPos: { lat: number; lon: number } | null = null; + await expect + .poll(async () => { + const p = (await getState(page)).planes.find((x) => x.id === 'plane-starter'); + if (!p || p.status !== 'flying') return false; + if (prevPos && prevPos.lat === p.lat && prevPos.lon === p.lon) return true; + prevPos = { lat: p.lat, lon: p.lon }; + return false; + }, { timeout: 15_000, message: 'waiting for the paused clock to freeze the plane position' }) + .toBe(true); + + // Fetch + project in one evaluate — with the sim paused above, the server's analytic + // position is static, so the projected point matches what the map renders. const pt = await page.evaluate(async () => { const m = (window as any).__flightMap; if (!m) return null; @@ -277,6 +292,9 @@ test('plane markers move while flying and the plane popup shows status + route', // The popup's route line is the plane's own leg: origin name → destination name. await expect(page.getByTestId('plane-route')).toContainText(`${live.locationName} → ${live.destName}`); + // Resume before step 5 — the landing wait needs a running clock. + await postSim(page, { timeScale: SCALE, paused: false }); + // 5) Let the flight finish, then fly home so the live instance stays in its // onboarding state (starter grounded at EDDF) for the other specs. The loaded // package is delivered on arrival (in-deadline).