e2e: per-test timescale speedup — FAST/CHAIN rates for pure arrival waits (v1.8.0)
CI / build + unit + e2e (pull_request) Successful in 6m3s
CI / build + unit + e2e (pull_request) Successful in 6m3s
- actions/hover/two-planes: FAST=10_000 for expiry fast-forwards, landing waits and fly-home cleanups; queue: + CHAIN=4_000 for auto-chain observation, refuel test at explicit 2_000 with relaxed fuel bound - map-interactions: FAST=50_000 chain hops (~3 min -> ~15 s per direction); crossing leg stays at SCALE and now sets it BEFORE dispatch (a leftover fast rate would let the dispatch tick consume most of the leg) - hover fleet-row test asserts flying from the fly endpoint's own snapshot and freezes immediately instead of speeding up then freezing mid-leg - two-planes parallel-flight: both-grounded poll after cleanup dispatches so afterEach heal no longer pays the range-edge return leg at SCALE
This commit is contained in:
+26
-15
@@ -20,6 +20,12 @@ test.beforeEach(async ({ page }) => { await authenticate(page); });
|
||||
// the next queued leg server-side (part 1); test 3 exercises that chain end-to-end through the API.
|
||||
|
||||
const SCALE = 1000; // test rate for fast-forwarding legs — keeps flights to seconds of real time
|
||||
// Pure arrival waits (no mid-leg observation): LEMD-length legs land in ~1 s. Safe at any scale —
|
||||
// a lagged tick can only finish the leg sooner, never skip an observed state.
|
||||
const FAST = 10_000;
|
||||
// Chained-leg observation: max per-tick advance (2 s × S) must stay below the LEMD-length leg's
|
||||
// sim-duration (~1e7 sim-ms) so one lagged tick can't complete it before a poll sees the chain.
|
||||
const CHAIN = 4_000;
|
||||
const WORLD = 24; // the fixed world rate (ISS-117) — must match lib/worldClock.ts
|
||||
const HUB = 'EDDF'; // must match HUB_ICAO in lib/game/seed.ts
|
||||
const DEST = 'LEMD'; // Madrid–Barajas — the typical long leg (~10 s at scale 1000)
|
||||
@@ -285,14 +291,15 @@ test('queue display + cancel via UI (ISS-043 part 2)', async ({ page }) => {
|
||||
await expect(page.getByTestId('queue-leg-plane-starter-0'), 'the cancelled leg is removed from the queue').toHaveCount(0, { timeout: 15_000 });
|
||||
|
||||
// Cleanup: resume + fast-forward to arrival at DEST (empty queue → plain landing), then fly home.
|
||||
await setScale(page, SCALE);
|
||||
// Both waits are pure arrivals — FAST is safe and ~10× quicker than SCALE here.
|
||||
await setScale(page, FAST);
|
||||
await expect.poll(async () => {
|
||||
const p = (await getState(page)).planes.find((x) => x.id === 'plane-starter');
|
||||
return !!p && p.status === 'available' && p.locationIcao === DEST;
|
||||
}, { timeout: 120_000, message: `waiting for plane-starter to land at ${DEST}` }).toBe(true);
|
||||
|
||||
// Re-set SCALE for the return leg.
|
||||
await setScale(page, SCALE);
|
||||
// Re-set FAST for the return leg.
|
||||
await setScale(page, FAST);
|
||||
const home = await page.request.post('/api/planes/plane-starter/fly', { data: { destIcao: HUB } });
|
||||
expect(home.status()).toBe(200);
|
||||
await expect.poll(async () => ((await getState(page)).planes.find((p) => p.id === 'plane-starter')?.locationIcao ?? ''), {
|
||||
@@ -366,14 +373,15 @@ test('dotted queue line renders the queued path on the map (ISS-043 part 2)', as
|
||||
// to arrival, then fly home.
|
||||
const cancelRes = await page.request.post('/api/planes/plane-starter/queue/cancel', { data: { index: 0 } });
|
||||
expect(cancelRes.status()).toBe(200);
|
||||
await setScale(page, SCALE);
|
||||
// Pure arrival waits — FAST is safe and ~10× quicker than SCALE here.
|
||||
await setScale(page, FAST);
|
||||
await expect.poll(async () => {
|
||||
const p = (await getState(page)).planes.find((x) => x.id === 'plane-starter');
|
||||
return !!p && p.status === 'available' && p.locationIcao === DEST;
|
||||
}, { timeout: 120_000, message: `waiting for plane-starter to land at ${DEST}` }).toBe(true);
|
||||
|
||||
// Re-set SCALE for the return leg.
|
||||
await setScale(page, SCALE);
|
||||
// Re-set FAST for the return leg.
|
||||
await setScale(page, FAST);
|
||||
const home = await page.request.post('/api/planes/plane-starter/fly', { data: { destIcao: HUB } });
|
||||
expect(home.status()).toBe(200);
|
||||
await expect.poll(async () => ((await getState(page)).planes.find((p) => p.id === 'plane-starter')?.locationIcao ?? ''), {
|
||||
@@ -457,9 +465,9 @@ test('landing auto-starts the next queued leg (ISS-043 chain end-to-end)', async
|
||||
expect(flyRes.status()).toBe(200);
|
||||
|
||||
// Fast-forward (also clears any pause). On landing at DEST the server must AUTO-START the queued
|
||||
// return leg — no manual dispatch. The chained state persists for the whole ~10 s return leg, so a
|
||||
// poll can't miss it: status 'flying' with destIcao = the first (only) queued ICAO.
|
||||
await setScale(page, SCALE);
|
||||
// return leg — no manual dispatch. The chained state persists for the whole ~2.5 s return leg at
|
||||
// CHAIN, so a poll can't miss it: status 'flying' with destIcao = the first (only) queued ICAO.
|
||||
await setScale(page, CHAIN);
|
||||
await expect.poll(async () => {
|
||||
const p = (await getState(page)).planes.find((x) => x.id === 'plane-starter');
|
||||
return !!p && p.status === 'flying' && p.destIcao === HUB;
|
||||
@@ -566,8 +574,10 @@ test('a drained tank is topped up before a chained leg at exactly the rep-discou
|
||||
const rate = repRate(s0.account.reputation, FUEL_DISCOUNT_PER_REP, MAX_FUEL_DISCOUNT);
|
||||
const expectedCost = Math.round(liters * FUEL_PRICE_PER_L * (1 - rate) * 100) / 100;
|
||||
|
||||
// Landing at DEST auto-chains QUEUE with the top-up…
|
||||
await setScale(page, SCALE);
|
||||
// Landing at DEST auto-chains QUEUE with the top-up… 2_000× (not SCALE): leg 2's chained state must
|
||||
// stay observable — max per-tick advance (2 s × S) stays below its ~1e7 sim-ms duration, and the
|
||||
// snapshot window below is sized for this rate.
|
||||
await setScale(page, 2_000);
|
||||
await expect.poll(async () => {
|
||||
const x = (await getState(page)).planes.find((y) => y.id === 'plane-starter');
|
||||
return !!x && x.status === 'flying' && x.destIcao === QUEUE;
|
||||
@@ -577,9 +587,9 @@ test('a drained tank is topped up before a chained leg at exactly the rep-discou
|
||||
// Exact fuel equality is impossible — this snapshot lands after leg 2's departure burn; the exact top-up level × rep-discounted price is pinned by the cash assertion below.
|
||||
const fuel = s.planes.find((x) => x.id === 'plane-starter')!.fuel;
|
||||
expect(fuel).toBeLessThanOrEqual(target); // can only have burned since departure…
|
||||
// 1.3× not 1.4×: under dind load the snapshot can land ~0.1×required2 into leg 2's departure burn;
|
||||
// a wrong top-up level (≤ required2, or none at all) still lands far below this bound.
|
||||
expect(fuel).toBeGreaterThan(1.3 * required2);
|
||||
// > required2 (not 1.3×): at 2_000× the snapshot can land ~0.4×required2 into leg 2's burn under dind
|
||||
// load; a wrong top-up level (≤ required2, or none at all) still lands far below this bound.
|
||||
expect(fuel).toBeGreaterThan(required2);
|
||||
expect(s.account.cash).toBeCloseTo(cashBeforeChain - expectedCost, 6); // at exactly the rep-discounted cost
|
||||
|
||||
// Let leg 2 finish so the next test starts from a known airport.
|
||||
@@ -603,7 +613,8 @@ test('cancelling the only queued leg mid-flight leaves a plain landing (v0.46.0)
|
||||
const cancel = await page.request.post('/api/planes/plane-starter/queue/cancel', { data: { index: 0 } });
|
||||
expect(cancel.status()).toBe(200);
|
||||
|
||||
await setScale(page, SCALE);
|
||||
// Pure arrival wait (the queue is empty — nothing to observe mid-leg) → FAST.
|
||||
await setScale(page, FAST);
|
||||
await expect.poll(async () => {
|
||||
const x = (await getState(page)).planes.find((y) => y.id === 'plane-starter');
|
||||
return !!x && x.status === 'available' && x.locationIcao === DEST;
|
||||
|
||||
Reference in New Issue
Block a user