diff --git a/tests/ui/gpx-journey.spec.js b/tests/ui/gpx-journey.spec.js new file mode 100644 index 0000000..a74924f --- /dev/null +++ b/tests/ui/gpx-journey.spec.js @@ -0,0 +1,90 @@ +// @ts-check +// Tests: G1–G4 — buildJourneySegments algorithm correctness +// These tests load the italy-2025 map page (which has GPX) to get MapUtils in scope, +// then call the functions with synthetic data via page.evaluate. +// Requires demo data: run `make demo-load` before this suite. +const { test, expect } = require('@playwright/test'); + +async function getMapUtils(page) { + await page.goto('/trips/italy-2025/map'); + await expect(page.locator('canvas.maplibregl-canvas')).toBeVisible({ timeout: 10000 }); +} + +// G1: No GPX → all pairs connected in one segment +test('G1: all markers connected when no GPX files present', async ({ page }) => { + await getMapUtils(page); + + var count = await page.evaluate(function () { + var entries = [ + { lat: '43.0', lng: '11.0', force_connect: false }, + { lat: '44.0', lng: '12.0', force_connect: false }, + { lat: '45.0', lng: '13.0', force_connect: false } + ]; + return MapUtils.buildJourneySegments(entries, [], 10).length; + }); + + expect(count).toBe(1); +}); + +// G2: Same GPX file covers both markers → connector suppressed (0 segments) +test('G2: connector suppressed when same GPX file covers both markers', async ({ page }) => { + await getMapUtils(page); + + var count = await page.evaluate(function () { + var e1 = { lat: '43.000', lng: '11.000', force_connect: false }; + var e2 = { lat: '43.010', lng: '11.010', force_connect: false }; + // Trackpoints covering both (stored as [lat, lng]) + var track = [[43.000, 11.000], [43.005, 11.005], [43.010, 11.010]]; + return MapUtils.buildJourneySegments([e1, e2], [track], 10).length; + }); + + expect(count).toBe(0); +}); + +// G3: force_connect overrides GPX suppression +test('G3: force_connect keeps connector even when GPX covers both markers', async ({ page }) => { + await getMapUtils(page); + + var count = await page.evaluate(function () { + var e1 = { lat: '43.000', lng: '11.000', force_connect: false }; + var e2 = { lat: '43.010', lng: '11.010', force_connect: true }; + var track = [[43.000, 11.000], [43.005, 11.005], [43.010, 11.010]]; + return MapUtils.buildJourneySegments([e1, e2], [track], 10).length; + }); + + expect(count).toBe(1); +}); + +// G4: Markers near DIFFERENT GPX files → connector kept +test('G4: connector kept when markers are near different GPX files', async ({ page }) => { + await getMapUtils(page); + + var count = await page.evaluate(function () { + var e1 = { lat: '43.000', lng: '11.000', force_connect: false }; + var e2 = { lat: '45.000', lng: '13.000', force_connect: false }; + // Two separate files — each only covers one marker + var trackA = [[43.000, 11.000], [43.005, 11.005]]; // near e1 only + var trackB = [[45.000, 13.000], [45.005, 13.005]]; // near e2 only + return MapUtils.buildJourneySegments([e1, e2], [trackA, trackB], 10).length; + }); + + expect(count).toBe(1); +}); + +// G5: First pair suppressed, second pair kept → one segment [e2, e3] +test('G5: suppressed first pair leaves one segment from e2 to e3', async ({ page }) => { + await getMapUtils(page); + + var count = await page.evaluate(function () { + // e1→e2: covered by track → suppressed; e1 is orphaned (< 2 pts, not pushed) + // e2→e3: not covered → connector kept → segment [e2, e3] + var e1 = { lat: '43.000', lng: '11.000', force_connect: false }; + var e2 = { lat: '43.010', lng: '11.010', force_connect: false }; + var e3 = { lat: '45.000', lng: '13.000', force_connect: false }; + var track = [[43.000, 11.000], [43.005, 11.005], [43.010, 11.010]]; // covers e1 and e2 only + var segs = MapUtils.buildJourneySegments([e1, e2, e3], [track], 10); + return segs.length; + }); + + expect(count).toBe(1); // one segment: [e2 → e3] +});