On-board package hover traces from the carrier's current position (ISS-040)
CI / build + unit + e2e (pull_request) Successful in 7m11s
CI / build + unit + e2e (pull_request) Successful in 7m11s
This commit is contained in:
@@ -24,6 +24,8 @@ interface PlaneState {
|
||||
maxFuel: number;
|
||||
locationIcao: string;
|
||||
destIcao: string | null;
|
||||
lat: number; // live position — interpolated per snapshot while flying, airport coords when grounded
|
||||
lon: number;
|
||||
}
|
||||
|
||||
interface PackageState {
|
||||
@@ -496,3 +498,95 @@ test('hovering an on-board block of a flying plane highlights its route', async
|
||||
})
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
test('hovering an on-board package while its carrier is mid-flight traces from current position', async ({ page }) => {
|
||||
test.setTimeout(600_000); // self-heal + one flight at scale 1000 + fly-home cleanup
|
||||
await flyHomeViaApi(page);
|
||||
|
||||
// Full tank before departure — the EDDF↔LEMD round trip burns ~4970 L of the 6300 L tank.
|
||||
const refuel = await page.request.post('/api/planes/plane-starter/refuel');
|
||||
expect(refuel.status()).toBe(200);
|
||||
|
||||
await page.goto('/');
|
||||
await waitForAirportCount(page);
|
||||
|
||||
// Load + fly via the API (same pattern as e2e/map-interactions.spec.ts), then speed up.
|
||||
const pkg = await createPackage(page, HUB, DEST);
|
||||
const load = await page.request.post(`/api/packages/${pkg.id}/load`, { data: { planeId: 'plane-starter' } });
|
||||
expect(load.status()).toBe(200);
|
||||
const fly = await page.request.post('/api/planes/plane-starter/fly', { data: { destIcao: DEST } });
|
||||
expect(fly.status()).toBe(200);
|
||||
await setScale(page, SCALE);
|
||||
|
||||
// Wait until the starter is GENUINELY mid-flight — its live position clearly away from EDDF in
|
||||
// both axes (well past HOVER_ROUTE_EPS_DEG) so "the trace starts at the plane" and "it starts at
|
||||
// the pickup airport" are distinguishable. The ~10 s leg at scale 1000 gives a wide window.
|
||||
const hubLonLat = await airportLonLat(page, HUB);
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const p = (await getState(page)).planes.find((x) => x.id === 'plane-starter');
|
||||
if (!p || p.status !== 'flying') return false;
|
||||
return Math.abs(p.lon - hubLonLat[0]) > 0.2 && Math.abs(p.lat - hubLonLat[1]) > 0.2;
|
||||
}, { timeout: 60_000, message: 'waiting for plane-starter to be clearly away from the hub' })
|
||||
.toBe(true);
|
||||
|
||||
// Freeze mid-flight so the position is stable while we assert (same pause pattern as the
|
||||
// flying-plane hover test above): paused, every /api/state snapshot reports the same lat/lon.
|
||||
await postSim(page, { paused: true });
|
||||
|
||||
const row = page.locator(`[data-testid="onboard-package-${pkg.id}"]`);
|
||||
await expect(row, 'the loaded package appears in the On board section').toBeVisible({ timeout: 15_000 });
|
||||
|
||||
// Both endpoints must be in-viewport for queryRenderedFeatures to see the trace.
|
||||
await fitToAirports(page, [HUB, DEST]);
|
||||
|
||||
const live = (await getState(page)).planes.find((x) => x.id === 'plane-starter')!;
|
||||
const liveLonLat: [number, number] = [live.lon, live.lat]; // stable — the sim is paused
|
||||
const destLonLat = await airportLonLat(page, DEST);
|
||||
|
||||
await row.hover();
|
||||
// ISS-040 (v0.23.2): the trace starts at the carrier's LIVE position and ends at the destination,
|
||||
// and NO segment may start near the pickup airport (the old origin-based behavior). Same per-tile
|
||||
// clipping/quantization caveats as expectHoverRoute — check endpoints across all segments with EPS.
|
||||
await expect.poll(async () => {
|
||||
const feats = (await renderedFeatures(page, ['hover-route'])) as Array<{ geometry?: { type: string; coordinates: [number, number][] } }>;
|
||||
const segs = feats.filter(
|
||||
(f): f is { geometry: { type: 'LineString'; coordinates: [number, number][] } } =>
|
||||
!!f.geometry && f.geometry.type === 'LineString' && Array.isArray(f.geometry.coordinates) && f.geometry.coordinates.length >= 2,
|
||||
);
|
||||
if (!segs.length) return false;
|
||||
const near = (a: [number, number], b: [number, number]) =>
|
||||
Math.abs(a[0] - b[0]) <= HOVER_ROUTE_EPS_DEG && Math.abs(a[1] - b[1]) <= HOVER_ROUTE_EPS_DEG;
|
||||
const startsAtLive = segs.some((f) => near(f.geometry.coordinates[0], liveLonLat));
|
||||
const endsAtDest = segs.some((f) => near(f.geometry.coordinates[f.geometry.coordinates.length - 1], destLonLat));
|
||||
const noneStartsAtHub = !segs.some((f) => near(f.geometry.coordinates[0], hubLonLat));
|
||||
return startsAtLive && endsAtDest && noneStartsAtHub;
|
||||
}, { timeout: 15_000, message: 'waiting for the hover-route trace to run from the live position to the destination' }).toBe(true);
|
||||
|
||||
await unhover(page);
|
||||
await expect.poll(async () => (await renderedFeatures(page, ['hover-route'])).length, {
|
||||
timeout: 15_000,
|
||||
message: 'waiting for the hover route trace to clear after mouseleave',
|
||||
}).toBe(0);
|
||||
|
||||
// Resume and fast-forward to arrival — landing at DEST auto-delivers the package (v0.12.0), so
|
||||
// flying home afterwards leaves the starter clean for the other specs.
|
||||
await postSim(page, { timeScale: SCALE, paused: false });
|
||||
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);
|
||||
|
||||
// ISS-011: the arrival above auto-slown the sim to 1×, so re-set SCALE for this leg.
|
||||
await setScale(page, SCALE);
|
||||
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 === HUB, {
|
||||
timeout: 120_000,
|
||||
message: 'waiting for plane-starter to land back at the hub',
|
||||
})
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user