fix(e2e): expectHoverRoute — ignore queryRenderedFeatures artifacts without LineString geometry (CI run #70)
CI / build + unit + e2e (pull_request) Failing after 7m49s

This commit is contained in:
2026-09-20 01:22:23 +00:00
parent 2655ef81c8
commit 505a58b4fe
2 changed files with 16 additions and 5 deletions
+15 -5
View File
@@ -182,7 +182,10 @@ async function airportLonLat(page: Page, icao: string): Promise<[number, number]
// exact-coords comparison can therefore never match even when the line is drawn correctly (CI run #69:
// all three hover-route timeouts while the trace was visibly rendered), so we accept any number of
// segments and require some segment to START within EPS of `from` and some to END within EPS of `to`.
// The true endpoints survive clipping verbatim in whichever tile contains them. Call
// The true endpoints survive clipping verbatim in whichever tile contains them. CI run #70
// additionally showed queryRenderedFeatures can return per-tile artifact features WITHOUT a usable
// geometry at all (f.geometry undefined), so the helper filters to real LineString segments and
// treats "none yet" as keep-polling instead of throwing inside the poll callback. Call
// fitToAirports([from, to]) first — rendered features are viewport-scoped.
const HOVER_ROUTE_EPS_DEG = 0.05; // ~5 km: above worst-case quantization at any zoom, below the distance between any two EU airports
@@ -191,11 +194,18 @@ async function expectHoverRoute(page: Page, fromIcao: string, toIcao: string): P
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;
await expect.poll(async () => {
const feats = (await renderedFeatures(page, ['hover-route'])) as Array<{ geometry: { coordinates: [number, number][] } }>;
if (!feats.length) return false;
const feats = (await renderedFeatures(page, ['hover-route'])) as Array<{ geometry?: { type: string; coordinates: [number, number][] } }>;
// CI run #70: MapLibre can return per-tile artifacts WITHOUT a usable LineString geometry
// (f.geometry undefined). A throw inside an expect.poll callback fails the test instantly
// instead of retrying — so keep only real line segments; "none yet" means keep polling.
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;
// The line may be clipped into several per-tile segments — check the endpoints across all of them.
const startsAtFrom = feats.some((f) => near(f.geometry.coordinates[0], from));
const endsAtTo = feats.some((f) => {
const startsAtFrom = segs.some((f) => near(f.geometry.coordinates[0], from));
const endsAtTo = segs.some((f) => {
const c = f.geometry.coordinates;
return near(c[c.length - 1], to);
});