import { test, expect } from '@playwright/test'; // MapLibre GL requires WebGL which isn't available in headless Chromium. // Intercept the module request and return a lightweight mock so React can render. const MAPLIBRE_MOCK = ` class Map { constructor(opts) { this._listeners = {}; Promise.resolve().then(() => this._emit('style.load')); } _emit(event) { (this._listeners[event] || []).forEach(fn => fn()); } on(event, fn) { if (!this._listeners[event]) this._listeners[event] = []; this._listeners[event].push(fn); return this; } off(event, fn) { this._listeners[event] = (this._listeners[event] || []).filter(l => l !== fn); return this; } remove() {} getSource() { return null; } getLayer() { return null; } addSource() {} addLayer() {} removeLayer() {} removeSource() {} setSky() {} setTerrain() {} setLayoutProperty() {} flyTo() {} jumpTo() {} setStyle() { Promise.resolve().then(() => this._emit('style.load')); } setProjection() {} hasImage() { return false; } addImage() {} getCanvas() { return document.createElement('canvas'); } project() { return { x: 0, y: 0 }; } unproject() { return { lng: 0, lat: 0 }; } } class Marker { constructor(opts) { this._el = (opts && opts.element) || document.createElement('div'); } setLngLat() { return this; } addTo() { return this; } remove() {} getLngLat() { return { lng: 0, lat: 0 }; } getElement() { return this._el; } } class Popup { setLngLat() { return this; } setDOMContent() { return this; } addTo() { return this; } remove() {} } class LngLatBounds { extend() { return this; } } export default { Map, Marker, Popup, LngLatBounds }; `; test.beforeEach(async ({ page }) => { await page.route('**maplibre-gl.js*', async route => { await route.fulfill({ contentType: 'application/javascript', body: MAPLIBRE_MOCK }); }); }); test.describe('Line of Sight Application', () => { test('should load the home page and show settings', async ({ page }) => { await page.goto('/'); await expect(page.locator('text=Line of Sight Settings')).toBeVisible(); await expect(page.locator('label:has-text("Direction")')).toBeVisible(); }); test('should be able to toggle map style', async ({ page }) => { await page.goto('/'); await page.waitForSelector('h3:text("Line of Sight Settings")', { timeout: 10000 }); const darkButton = page.locator('button:text("Dark")'); await darkButton.click(); await expect(darkButton).toHaveClass(/active-style/); }); test('should show results when clicking the search button', async ({ page }) => { await page.goto('/'); await page.waitForSelector('h3:text("Line of Sight Settings")', { timeout: 10000 }); await page.route('**/api/line-of-sight*', async route => { const json = { success: true, data: { conurbations: [ { id: 1, name: 'Test City', population: 100000, country: 'TS', lat: 0, lon: 0, distance_km: 10, off_line_km: 1 } ], line_coordinates: [{ lat: 0, lon: 0 }, { lat: 1, lon: 1 }] } }; await route.fulfill({ json }); }); await page.click('button:text("Show Line of Sight")'); await expect(page.locator('text=Conurbations Found')).toBeVisible(); await expect(page.locator('text=Test City')).toBeVisible(); }); });