|
|
|
|
@@ -1,178 +1,46 @@
|
|
|
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper: Pan the Leaflet map by simulating a mouse drag and wait until
|
|
|
|
|
* mapCenter in localStorage changes (MapComponent persists center on move/zoom).
|
|
|
|
|
*
|
|
|
|
|
* Note for codegen: The recorder oft erfasst Klicks statt Drag. Ersetze
|
|
|
|
|
* die aufgenommenen Klicks nachträglich durch diesen Helper für verlässliches Panning.
|
|
|
|
|
*/
|
|
|
|
|
async function panMap(page, deltaY = -200) {
|
|
|
|
|
const map = page.locator("#map");
|
|
|
|
|
await map.waitFor({ state: "visible" });
|
|
|
|
|
// Wait until Leaflet initializes the container class
|
|
|
|
|
await page.locator("#map.leaflet-container").waitFor({ state: "visible", timeout: 20_000 });
|
|
|
|
|
|
|
|
|
|
const centerBefore = await page.evaluate(() => {
|
|
|
|
|
try {
|
|
|
|
|
const v = localStorage.getItem("mapCenter");
|
|
|
|
|
return v ? JSON.parse(v) : null;
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
// Helper: robust selection for native <select> or custom ARIA comboboxes
|
|
|
|
|
async function selectStation(page, value) {
|
|
|
|
|
// Try to find by accessible name first
|
|
|
|
|
let combo = page.getByRole("combobox", { name: /Station wählen/i });
|
|
|
|
|
if (!(await combo.count())) {
|
|
|
|
|
// Fallback: find a container with the label text and locate a select inside
|
|
|
|
|
const container = page.locator("div").filter({ hasText: "Station wählen" }).last();
|
|
|
|
|
const selectInContainer = container.locator("select");
|
|
|
|
|
if (await selectInContainer.count()) {
|
|
|
|
|
combo = selectInContainer.first();
|
|
|
|
|
} else {
|
|
|
|
|
// Final fallback: first visible native select (overlay has only one)
|
|
|
|
|
combo = page.locator("select:visible").first();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const box = await map.boundingBox();
|
|
|
|
|
if (!box) throw new Error("Map bounding box not found");
|
|
|
|
|
const startX = box.x + box.width / 2;
|
|
|
|
|
const startY = box.y + box.height / 2;
|
|
|
|
|
|
|
|
|
|
await page.mouse.move(startX, startY);
|
|
|
|
|
await page.mouse.down();
|
|
|
|
|
await page.mouse.move(startX, startY + deltaY, { steps: 12 });
|
|
|
|
|
await page.mouse.up();
|
|
|
|
|
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
prev => {
|
|
|
|
|
try {
|
|
|
|
|
const v = localStorage.getItem("mapCenter");
|
|
|
|
|
if (!v) return false;
|
|
|
|
|
if (!prev) return true; // previously null -> now set
|
|
|
|
|
const c = JSON.parse(v);
|
|
|
|
|
const dLat = Math.abs(c[0] - prev[0]);
|
|
|
|
|
const dLng = Math.abs(c[1] - prev[1]);
|
|
|
|
|
return dLat > 0.0005 || dLng > 0.0005;
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
centerBefore,
|
|
|
|
|
{ timeout: 15_000 }
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
await expect(combo).toBeVisible();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper: Move mouse to the visual center of the #map element.
|
|
|
|
|
*/
|
|
|
|
|
async function moveMouseToMapCenter(page) {
|
|
|
|
|
const map = page.locator("#map");
|
|
|
|
|
await map.waitFor({ state: "visible" });
|
|
|
|
|
await page.locator("#map.leaflet-container").waitFor({ state: "visible", timeout: 20_000 });
|
|
|
|
|
const box = await map.boundingBox();
|
|
|
|
|
if (!box) throw new Error("Map bounding box not found");
|
|
|
|
|
const cx = box.x + box.width / 2;
|
|
|
|
|
const cy = box.y + box.height / 2;
|
|
|
|
|
await page.mouse.move(cx, cy);
|
|
|
|
|
return { cx, cy };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper: Read map zoom from localStorage as a number. Returns NaN if not set.
|
|
|
|
|
*/
|
|
|
|
|
async function getZoomFromLocalStorage(page) {
|
|
|
|
|
return await page.evaluate(() => {
|
|
|
|
|
const v = localStorage.getItem("mapZoom");
|
|
|
|
|
return v ? Number(v) : NaN;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper: Wait until mapZoom in localStorage changes compared to a previous value.
|
|
|
|
|
* Optionally enforce a direction via compareFn(newZoom, oldZoom) => boolean.
|
|
|
|
|
*/
|
|
|
|
|
async function waitForZoomChange(page, previousZoom, compareFn) {
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
(prev, cmp) => {
|
|
|
|
|
const v = localStorage.getItem("mapZoom");
|
|
|
|
|
if (!v) return false;
|
|
|
|
|
const z = Number(v);
|
|
|
|
|
if (Number.isNaN(z)) return false;
|
|
|
|
|
if (typeof cmp === "string" && cmp === "gt") return z > prev;
|
|
|
|
|
if (typeof cmp === "string" && cmp === "lt") return z < prev;
|
|
|
|
|
if (typeof cmp === "string" && cmp === "ne") return z !== prev;
|
|
|
|
|
return z !== prev;
|
|
|
|
|
},
|
|
|
|
|
previousZoom,
|
|
|
|
|
{ timeout: 10_000 }
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper: Zoom the map using wheel input at map center.
|
|
|
|
|
* Uses page.mouse.wheel when available; otherwise dispatches a WheelEvent in the page context.
|
|
|
|
|
* Negative deltaY zooms in; positive deltaY zooms out.
|
|
|
|
|
*/
|
|
|
|
|
async function wheelZoom(page, deltaY = -400) {
|
|
|
|
|
const { cx, cy } = await moveMouseToMapCenter(page);
|
|
|
|
|
// Try native Playwright wheel first
|
|
|
|
|
try {
|
|
|
|
|
await page.mouse.wheel(0, deltaY);
|
|
|
|
|
return;
|
|
|
|
|
} catch {
|
|
|
|
|
// Fallback: dispatch a real WheelEvent at the map element
|
|
|
|
|
await page.evaluate(
|
|
|
|
|
({ selector, deltaY, cx, cy }) => {
|
|
|
|
|
const el = document.querySelector(selector);
|
|
|
|
|
if (!el) return false;
|
|
|
|
|
const evt = new WheelEvent("wheel", {
|
|
|
|
|
deltaY,
|
|
|
|
|
clientX: cx,
|
|
|
|
|
clientY: cy,
|
|
|
|
|
bubbles: true,
|
|
|
|
|
cancelable: true,
|
|
|
|
|
view: window,
|
|
|
|
|
});
|
|
|
|
|
el.dispatchEvent(evt);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
{ selector: "#map", deltaY, cx, cy }
|
|
|
|
|
);
|
|
|
|
|
const isNative = await combo.evaluate(el => el.tagName === "SELECT");
|
|
|
|
|
if (isNative) {
|
|
|
|
|
await expect(combo).toBeEnabled();
|
|
|
|
|
await expect(combo.locator(`option[value="${value}"]`)).toBeAttached();
|
|
|
|
|
await combo.selectOption({ value });
|
|
|
|
|
} else {
|
|
|
|
|
await combo.click();
|
|
|
|
|
await page.getByRole("option", { name: new RegExp(value) }).click();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Attach localStorage snapshot on failure to aid debugging
|
|
|
|
|
test.afterEach(async ({ page }, testInfo) => {
|
|
|
|
|
// Only capture on unexpected failure
|
|
|
|
|
if (testInfo.status !== testInfo.expectedStatus) {
|
|
|
|
|
const snapshot = await page.evaluate(() => {
|
|
|
|
|
const keys = [
|
|
|
|
|
"mapZoom",
|
|
|
|
|
"mapCenter",
|
|
|
|
|
"showAppInfoCard",
|
|
|
|
|
"showCoordinateInput",
|
|
|
|
|
"showLayersPanel",
|
|
|
|
|
"showAreaDropdown",
|
|
|
|
|
"currentMapId",
|
|
|
|
|
"currentUserId",
|
|
|
|
|
];
|
|
|
|
|
const out = {};
|
|
|
|
|
for (const k of keys) out[k] = localStorage.getItem(k);
|
|
|
|
|
return out;
|
|
|
|
|
});
|
|
|
|
|
// Attach as artifact and also log for convenience
|
|
|
|
|
await testInfo.attach("localStorage.json", {
|
|
|
|
|
contentType: "application/json",
|
|
|
|
|
body: JSON.stringify(snapshot, null, 2),
|
|
|
|
|
});
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.log("[localStorage snapshot]", snapshot);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("MapComponent", async ({ page }) => {
|
|
|
|
|
await page.goto("http://localhost:3000/?m=12&u=484");
|
|
|
|
|
// 10 Sekunden warten, bis die Karte sichtbar ist
|
|
|
|
|
await page.locator("#map").waitFor({ state: "visible", timeout: 10_000 });
|
|
|
|
|
// Set your keys before the page loads (from your screenshot)
|
|
|
|
|
// Set initial localStorage BEFORE navigation so the app reads them on load
|
|
|
|
|
await page.addInitScript(() => {
|
|
|
|
|
localStorage.setItem("editMode", "false");
|
|
|
|
|
localStorage.setItem("polylineVisible_m12_u484", "true");
|
|
|
|
|
localStorage.setItem("currentMapId", "12");
|
|
|
|
|
localStorage.setItem("currentUserId", "484");
|
|
|
|
|
localStorage.setItem("mapZoom", "13");
|
|
|
|
|
localStorage.setItem("kabelstreckenVisible", "false"); // legacy, not used for init
|
|
|
|
|
localStorage.setItem("kabelstreckenVisible", "false");
|
|
|
|
|
localStorage.setItem("showBaseMapPanel", "false");
|
|
|
|
|
localStorage.setItem("mapLayersVisibility_m12_u484", {
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
"mapLayersVisibility_m12_u484",
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
"system-1": true,
|
|
|
|
|
"system-2": false,
|
|
|
|
|
"system-3": false,
|
|
|
|
|
@@ -189,114 +57,54 @@ test("MapComponent", async ({ page }) => {
|
|
|
|
|
"system-110": false,
|
|
|
|
|
"system-111": false,
|
|
|
|
|
"system-200": false,
|
|
|
|
|
});
|
|
|
|
|
localStorage.setItem("mapCenter", [53.23938294961826, 8.21434020996094]);
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
localStorage.setItem("mapCenter", JSON.stringify([53.23938294961826, 8.21434020996094]));
|
|
|
|
|
localStorage.setItem("markerLink", "undefined");
|
|
|
|
|
localStorage.setItem("lastElementType", "marker");
|
|
|
|
|
localStorage.setItem("polylineVisible", "false"); // global legacy
|
|
|
|
|
localStorage.setItem("polylineVisible", "false");
|
|
|
|
|
localStorage.setItem("showAppInfoCard", "false");
|
|
|
|
|
localStorage.setItem("showCoordinateInput", "false");
|
|
|
|
|
localStorage.setItem("showLayersPanel", "false");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// test steps
|
|
|
|
|
await expect(
|
|
|
|
|
page.locator("div").filter({ hasText: "TALASKabelstreckenULAFGSM" }).nth(3)
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Layer-Panel ausblenden" }).click();
|
|
|
|
|
// 1) Navigate and wait for the map
|
|
|
|
|
await page.goto("http://localhost:3000/?m=12&u=484");
|
|
|
|
|
await page.locator("#map").waitFor({ state: "visible", timeout: 20_000 });
|
|
|
|
|
|
|
|
|
|
// 2) Optional: verify a key from localStorage at runtime
|
|
|
|
|
await expect(page.evaluate(() => localStorage.getItem("showLayersPanel"))).resolves.toBe("false");
|
|
|
|
|
|
|
|
|
|
// 3) Layer-Panel toggle: expect "einblenden" first (since showLayersPanel=false), then toggle
|
|
|
|
|
await expect(page.getByRole("button", { name: "Layer-Panel einblenden" })).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Layer-Panel einblenden" }).click();
|
|
|
|
|
await expect(
|
|
|
|
|
page.locator("div").filter({ hasText: "TALASKabelstreckenULAFGSM" }).nth(3)
|
|
|
|
|
).toBeVisible();
|
|
|
|
|
await expect(page.getByRole("button", { name: "Layer-Panel ausblenden" })).toBeVisible();
|
|
|
|
|
// 4) Collapse again to restore state
|
|
|
|
|
await page.getByRole("button", { name: "Layer-Panel ausblenden" }).click();
|
|
|
|
|
|
|
|
|
|
// 5) Info-Card toggle: start hidden -> show -> hide -> show again
|
|
|
|
|
await expect(page.getByRole("button", { name: "Info einblenden" })).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Info einblenden" }).click();
|
|
|
|
|
await expect(page.getByRole("button", { name: "Info ausblenden" })).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Info ausblenden" }).click();
|
|
|
|
|
await expect(page.getByRole("button", { name: "Info einblenden" })).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Info einblenden" }).click();
|
|
|
|
|
await expect(page.locator("div").filter({ hasText: "TALAS.Map Version" }).nth(3)).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Info ausblenden" }).click();
|
|
|
|
|
// Warten, bis der Button-Text wechselt, bevor erneut geklickt wird
|
|
|
|
|
await expect(page.getByRole("button", { name: "Info einblenden" })).toBeVisible({
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
});
|
|
|
|
|
await page.getByRole("button", { name: "Info einblenden" }).click();
|
|
|
|
|
await expect(page.locator("div").filter({ hasText: "TALAS.Map Version" }).nth(3)).toBeVisible();
|
|
|
|
|
|
|
|
|
|
// 6) Koordinatensuche toggle
|
|
|
|
|
await page.getByRole("button", { name: "Koordinatensuche einblenden" }).click();
|
|
|
|
|
await expect(page.locator("form")).toBeVisible();
|
|
|
|
|
await page.getByRole("button", { name: "Koordinatensuche ausblenden" }).click();
|
|
|
|
|
await page.getByRole("button", { name: "Info ausblenden" }).click();
|
|
|
|
|
|
|
|
|
|
// 7) Marker setzen und Stationen wählen
|
|
|
|
|
await page.getByLabel("Marker").click();
|
|
|
|
|
await expect(page.getByText("Station wählenBitte wählen…")).toBeVisible();
|
|
|
|
|
await page.getByRole("combobox").selectOption("50977");
|
|
|
|
|
await selectStation(page, "50977");
|
|
|
|
|
await page.getByLabel("Marker").click();
|
|
|
|
|
await page.getByRole("combobox").selectOption("50986");
|
|
|
|
|
await selectStation(page, "50986");
|
|
|
|
|
await page.getByLabel("Marker").click();
|
|
|
|
|
await page.getByLabel("Marker").click();
|
|
|
|
|
await page.getByLabel("Marker").click();
|
|
|
|
|
await page.getByRole("combobox").selectOption("50977");
|
|
|
|
|
await selectStation(page, "50977");
|
|
|
|
|
await page.getByRole("button", { name: "Karte auf Standardansicht" }).click();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("mouse wheel zoom updates mapZoom", async ({ page }) => {
|
|
|
|
|
// Set initial state before navigation so the app can read them on load
|
|
|
|
|
await page.addInitScript(() => {
|
|
|
|
|
localStorage.setItem("currentMapId", "12");
|
|
|
|
|
localStorage.setItem("currentUserId", "484");
|
|
|
|
|
localStorage.setItem("mapCenter", JSON.stringify([53.23938294961826, 8.21434020996094]));
|
|
|
|
|
localStorage.setItem("mapZoom", "10");
|
|
|
|
|
localStorage.setItem("showAppInfoCard", "false");
|
|
|
|
|
localStorage.setItem("showCoordinateInput", "false");
|
|
|
|
|
localStorage.setItem("showLayersPanel", "false");
|
|
|
|
|
localStorage.setItem("showAreaDropdown", "false");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await page.goto("http://localhost:3000/?m=12&u=484");
|
|
|
|
|
|
|
|
|
|
await page.locator("#map.leaflet-container").waitFor({ state: "visible", timeout: 20_000 });
|
|
|
|
|
|
|
|
|
|
const before = await getZoomFromLocalStorage(page);
|
|
|
|
|
expect(Number.isNaN(before)).toBeFalsy();
|
|
|
|
|
|
|
|
|
|
// Zoom in via wheel and expect zoom to increase
|
|
|
|
|
await wheelZoom(page, -600);
|
|
|
|
|
await waitForZoomChange(page, before, "gt");
|
|
|
|
|
const afterIn = await getZoomFromLocalStorage(page);
|
|
|
|
|
expect(afterIn).toBeGreaterThan(before);
|
|
|
|
|
|
|
|
|
|
// Zoom out via wheel and expect zoom to decrease
|
|
|
|
|
await wheelZoom(page, 800);
|
|
|
|
|
await waitForZoomChange(page, afterIn, "lt");
|
|
|
|
|
const afterOut = await getZoomFromLocalStorage(page);
|
|
|
|
|
expect(afterOut).toBeLessThan(afterIn);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("zoom control buttons update mapZoom", async ({ page }) => {
|
|
|
|
|
await page.addInitScript(() => {
|
|
|
|
|
localStorage.setItem("currentMapId", "12");
|
|
|
|
|
localStorage.setItem("currentUserId", "484");
|
|
|
|
|
localStorage.setItem("mapCenter", JSON.stringify([53.23938294961826, 8.21434020996094]));
|
|
|
|
|
localStorage.setItem("mapZoom", "10");
|
|
|
|
|
localStorage.setItem("showAppInfoCard", "false");
|
|
|
|
|
localStorage.setItem("showCoordinateInput", "false");
|
|
|
|
|
localStorage.setItem("showLayersPanel", "false");
|
|
|
|
|
localStorage.setItem("showAreaDropdown", "false");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await page.goto("http://localhost:3000/?m=12&u=484");
|
|
|
|
|
// Wait for Leaflet map and custom zoom controls
|
|
|
|
|
await page.locator("#map.leaflet-container").waitFor({ state: "visible", timeout: 20_000 });
|
|
|
|
|
const zoomInBtn = page.getByTestId("zoom-in");
|
|
|
|
|
const zoomOutBtn = page.getByTestId("zoom-out");
|
|
|
|
|
await expect(zoomInBtn).toBeVisible();
|
|
|
|
|
await expect(zoomOutBtn).toBeVisible();
|
|
|
|
|
|
|
|
|
|
const z0 = await getZoomFromLocalStorage(page);
|
|
|
|
|
await zoomInBtn.click();
|
|
|
|
|
await waitForZoomChange(page, z0, "gt");
|
|
|
|
|
const z1 = await getZoomFromLocalStorage(page);
|
|
|
|
|
expect(z1).toBeGreaterThan(z0);
|
|
|
|
|
|
|
|
|
|
await zoomOutBtn.click();
|
|
|
|
|
await waitForZoomChange(page, z1, "lt");
|
|
|
|
|
const z2 = await getZoomFromLocalStorage(page);
|
|
|
|
|
expect(z2).toBeLessThan(z1);
|
|
|
|
|
});
|
|
|
|
|
|