fix: v0.2.1 — clamp map zoom to 3–14
This commit is contained in:
@@ -4,6 +4,10 @@ import { test, expect, type Page } from '@playwright/test';
|
||||
// the terrain code path stays toggleable on by default in the app.
|
||||
const APP_URL = '/?terrain=0';
|
||||
|
||||
// Must match MIN_ZOOM / MAX_ZOOM exported from components/Map.tsx.
|
||||
const MIN_ZOOM = 3;
|
||||
const MAX_ZOOM = 14;
|
||||
|
||||
async function waitForAirportCount(page: Page): Promise<number> {
|
||||
const countEl = page.getByTestId('airport-count');
|
||||
await expect.poll(async () => {
|
||||
@@ -54,3 +58,77 @@ test('clicking an airport marker opens a popup with name and ICAO', async ({ pag
|
||||
await expect(popup).toContainText('Bornholm Airport');
|
||||
await expect(popup).toContainText('EKRN');
|
||||
});
|
||||
|
||||
test('wheel zoom is clamped between min and max zoom', async ({ page }) => {
|
||||
// Zoom clamping is pure camera behavior, so block basemap tile fetches (the
|
||||
// style JSON still loads): it keeps the headless main thread idle and makes the
|
||||
// test fast and independent of tile-server latency.
|
||||
await page.route('**://*.openfreemap.org/**', (route) => {
|
||||
if (route.request().url().includes('/styles/')) return route.continue();
|
||||
return route.abort();
|
||||
});
|
||||
await page.goto(APP_URL);
|
||||
|
||||
const getZoom = () => page.evaluate(() => (window as any).__flightMap?.getZoom() ?? -1);
|
||||
// Wait until the map instance exists and its style is fully applied, so the
|
||||
// camera state is stable before we start wheeling.
|
||||
await expect.poll(
|
||||
() =>
|
||||
page.evaluate(() => {
|
||||
const m = (window as any).__flightMap;
|
||||
return m && m.isStyleLoaded() ? m.getZoom() : -1;
|
||||
}),
|
||||
{ timeout: 15_000 },
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
// Wheel events must land on the canvas.
|
||||
const box = (await page.getByTestId('map').boundingBox())!;
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
|
||||
// Position the camera close to a limit so the wheeling under test has short
|
||||
// travel — headless tile rendering is slow across many zoom levels.
|
||||
const jumpTo = async (zoom: number) => {
|
||||
await page.evaluate((z) => (window as any).__flightMap.jumpTo({ zoom: z }), zoom);
|
||||
await expect.poll(getZoom, { timeout: 5_000 }).toBeCloseTo(zoom);
|
||||
};
|
||||
|
||||
// MapLibre consumes all accumulated wheel delta in a single render frame, capped
|
||||
// at one zoom level per frame — so keep feeding large deltas while polling until
|
||||
// the camera reaches (or passes) the limit; frames consume them as they run.
|
||||
const wheelToLimit = async (deltaY: number, atLimit: (z: number) => boolean) => {
|
||||
await expect.poll(async () => {
|
||||
if ((await getZoom()) === -1) return 'pending';
|
||||
if (atLimit(await getZoom())) return 'done';
|
||||
for (let i = 0; i < 10; i++) await page.mouse.wheel(0, deltaY);
|
||||
return 'pending';
|
||||
}, { timeout: 20_000 }).toBe('done');
|
||||
};
|
||||
|
||||
// Wait until the camera stops moving so we assert on the settled value, not a
|
||||
// mid-animation one (each wheel eases for ~200ms).
|
||||
const waitForStableZoom = async () => {
|
||||
let prev = await getZoom();
|
||||
let stable = 0;
|
||||
await expect.poll(async () => {
|
||||
const z = await getZoom();
|
||||
stable = Math.abs(z - prev) < 1e-9 ? stable + 1 : 0;
|
||||
prev = z;
|
||||
return stable >= 3 ? 'stable' : 'pending';
|
||||
}, { timeout: 5_000 }).toBe('stable');
|
||||
};
|
||||
|
||||
// Zoom in well past MAX_ZOOM with several large deltas.
|
||||
await jumpTo(MAX_ZOOM - 2);
|
||||
await wheelToLimit(-600, (z) => z >= MAX_ZOOM - 1e-9);
|
||||
await waitForStableZoom();
|
||||
expect(await getZoom()).toBeCloseTo(MAX_ZOOM);
|
||||
|
||||
// Zoom out well below MIN_ZOOM with several large deltas.
|
||||
await jumpTo(MIN_ZOOM + 1);
|
||||
await wheelToLimit(600, (z) => z <= MIN_ZOOM + 1e-9);
|
||||
await waitForStableZoom();
|
||||
const zoom = await getZoom();
|
||||
expect(zoom).toBeCloseTo(MIN_ZOOM);
|
||||
expect(zoom).toBeGreaterThanOrEqual(MIN_ZOOM);
|
||||
expect(zoom).toBeLessThanOrEqual(MAX_ZOOM);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user