200 lines
9.7 KiB
TypeScript
200 lines
9.7 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { mockWorkbenchContext } from "./support/mock-workbench-context";
|
|
|
|
test("keeps pipes and SCADA visible across fractional zoom changes", async ({ page }, testInfo) => {
|
|
await mockWorkbenchContext(page);
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await expect.poll(() => page.evaluate(() => Boolean(
|
|
window.__waterNetworkMap?.getLayer("pipes-flow")
|
|
&& window.__waterNetworkMap?.getLayer("scada-pressure-symbol")
|
|
)), { timeout: 15_000 }).toBe(true);
|
|
|
|
await page.evaluate(() => {
|
|
const map = window.__waterNetworkMap!;
|
|
map.addSource("scale-fixture", {
|
|
type: "geojson",
|
|
data: {
|
|
type: "FeatureCollection",
|
|
features: [
|
|
...[800, 400, 100, null].map((diameter, index) => ({
|
|
type: "Feature" as const,
|
|
id: `pipe-${index}`,
|
|
properties: { diameter, fixture_id: `pipe-${index}` },
|
|
geometry: {
|
|
type: "LineString" as const,
|
|
coordinates: [[121.46, 30.91 + index * 0.001], [121.47, 30.91 + index * 0.001]]
|
|
}
|
|
})),
|
|
...["pipe_flow", "pressure"].map((deviceType, index) => ({
|
|
type: "Feature" as const,
|
|
id: deviceType,
|
|
properties: { device_type: deviceType, fixture_id: deviceType },
|
|
geometry: {
|
|
type: "Point" as const,
|
|
coordinates: [121.462 + index * 0.008, 30.915]
|
|
}
|
|
}))
|
|
]
|
|
}
|
|
});
|
|
for (const id of ["pipes-casing", "pipes-flow", "pipes-hit", "scada-flow-symbol", "scada-pressure-symbol"]) {
|
|
const original = map.getStyle().layers.find((layer) => layer.id === id)!;
|
|
const copy = { ...original };
|
|
if ("source-layer" in copy) delete copy["source-layer"];
|
|
map.addLayer({
|
|
...copy,
|
|
id: `scale-fixture-${id}`,
|
|
source: "scale-fixture",
|
|
filter: ["all", ["==", ["geometry-type"], original.type === "line" ? "LineString" : "Point"],
|
|
"filter" in original ? original.filter ?? true : true]
|
|
});
|
|
}
|
|
map.jumpTo({ center: [121.465, 30.913], zoom: 11.7, pitch: 0, bearing: 0 });
|
|
});
|
|
|
|
const pipeIds = ["pipe-0", "pipe-1", "pipe-2", "pipe-3"];
|
|
for (const [zoom, scadaIds] of [
|
|
[11.7, ["pipe_flow", "pressure"]],
|
|
[12, ["pipe_flow", "pressure"]],
|
|
[13, ["pipe_flow", "pressure"]],
|
|
[14, ["pipe_flow", "pressure"]],
|
|
[16, ["pipe_flow", "pressure"]],
|
|
[11.7, ["pipe_flow", "pressure"]]
|
|
] as const) {
|
|
await page.evaluate((value) => window.__waterNetworkMap!.jumpTo({ zoom: value }), zoom);
|
|
await expect.poll(() => page.evaluate(() => {
|
|
const map = window.__waterNetworkMap!;
|
|
const ids = (layers: string[]) => [...new Set(
|
|
map.queryRenderedFeatures({ layers }).map((feature) => String(feature.properties.fixture_id))
|
|
)].sort();
|
|
return {
|
|
pipes: ids(["scale-fixture-pipes-flow"]),
|
|
hits: ids(["scale-fixture-pipes-hit"]),
|
|
scada: ids(["scale-fixture-scada-flow-symbol", "scale-fixture-scada-pressure-symbol"])
|
|
};
|
|
})).toEqual({ pipes: [...pipeIds], hits: [...pipeIds], scada: [...scadaIds] });
|
|
await page.screenshot({ path: testInfo.outputPath(`zoom-${zoom}.png`) });
|
|
}
|
|
});
|
|
|
|
for (const width of [1440, 390]) {
|
|
test(`declutters dense facilities and preserves selection at width ${width}`, async ({ page }, testInfo) => {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
await mockWorkbenchContext(page);
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await expect.poll(() => page.evaluate(() => Boolean(
|
|
window.__waterNetworkMap?.getLayer("pipes-flow")
|
|
)), { timeout: 15_000 }).toBe(true);
|
|
|
|
await page.evaluate(async () => {
|
|
const layerModuleUrl = "/src/features/workbench/map/layers.ts";
|
|
const { waterNetworkLayers } = await import(layerModuleUrl);
|
|
const scadaModuleUrl = "/src/features/workbench/map/scada.ts";
|
|
const { scadaLayers } = await import(scadaModuleUrl);
|
|
const layers = [...waterNetworkLayers, ...scadaLayers];
|
|
const map = window.__waterNetworkMap!;
|
|
map.addSource("density-fixture", {
|
|
type: "geojson",
|
|
promoteId: "fixture_id",
|
|
data: {
|
|
type: "FeatureCollection",
|
|
features: [
|
|
...Array.from({ length: 70 }, (_, index) => ({
|
|
type: "Feature" as const,
|
|
properties: { fixture_id: `valve-${index}`, kind: "valve" },
|
|
geometry: {
|
|
type: "Point" as const,
|
|
coordinates: [121.4635 + index % 10 * 0.00035, 30.912 + Math.floor(index / 10) * 0.00035]
|
|
}
|
|
})),
|
|
{
|
|
type: "Feature" as const,
|
|
properties: { fixture_id: "reservoir", kind: "reservoir" },
|
|
geometry: { type: "Point" as const, coordinates: [121.465, 30.913] }
|
|
},
|
|
...[
|
|
["pump", [121.464, 30.9114]],
|
|
["tank", [121.466, 30.9114]],
|
|
["water-quality", [121.465, 30.9098]],
|
|
["pressure", [121.505, 30.913]],
|
|
["junction", [121.4648, 30.9138]],
|
|
["gis-detail", [121.466, 30.9138]],
|
|
["gis-flow", [121.467, 30.9138]]
|
|
].map(([kind, coordinates]) => ({
|
|
type: "Feature" as const,
|
|
properties: { fixture_id: kind, kind },
|
|
geometry: { type: "Point" as const, coordinates }
|
|
}))
|
|
]
|
|
}
|
|
});
|
|
const kinds: Record<string, string> = {
|
|
"reservoirs-symbol": "reservoir", "pumps-symbol": "pump", "tanks-symbol": "tank",
|
|
"water_quality_monitoring_points-symbol": "water-quality",
|
|
"water_quality_monitoring_points-hit": "water-quality",
|
|
"scada-pressure-symbol": "pressure",
|
|
junctions: "junction", "junctions-hit": "junction",
|
|
"fx_20200423_hydrants-point": "gis-detail", "fx_20200423_hydrants-hit": "gis-detail",
|
|
"fx_20200423_flow_meters-point": "gis-flow", "fx_20200423_flow_meters-hit": "gis-flow"
|
|
};
|
|
for (const id of ["valves-casing", "valves", "valves-symbol", ...Object.keys(kinds),
|
|
"valves-selected-outer", "valves-selected", "valves-hit"]) {
|
|
const original = layers.find((layer: { id: string }) => layer.id === id);
|
|
const copy = { ...original };
|
|
delete copy["source-layer"];
|
|
map.addLayer({
|
|
...copy, id: `density-${id}`, source: "density-fixture",
|
|
filter: ["==", ["get", "kind"], kinds[id] ?? "valve"]
|
|
});
|
|
}
|
|
map.jumpTo({ center: [121.465, 30.913], zoom: 13.1, pitch: 0, bearing: 0 });
|
|
});
|
|
|
|
const renderedCounts = () => page.evaluate(() => {
|
|
const map = window.__waterNetworkMap!;
|
|
const count = (id: string) => new Set(
|
|
map.queryRenderedFeatures({ layers: [id] }).map((feature) => feature.properties.fixture_id)
|
|
).size;
|
|
return { dots: count("density-valves"), symbols: count("density-valves-symbol"), hits: count("density-valves-hit"),
|
|
reservoir: count("density-reservoirs-symbol"), pump: count("density-pumps-symbol"),
|
|
tank: count("density-tanks-symbol"), waterQuality: count("density-water_quality_monitoring_points-symbol"),
|
|
waterQualityHit: count("density-water_quality_monitoring_points-hit"), pressure: count("density-scada-pressure-symbol"),
|
|
nodes: count("density-junctions"), nodeHits: count("density-junctions-hit"),
|
|
gisDetails: count("density-fx_20200423_hydrants-point"), gisDetailHits: count("density-fx_20200423_hydrants-hit"),
|
|
gisFlow: count("density-fx_20200423_flow_meters-point"), gisFlowHits: count("density-fx_20200423_flow_meters-hit") };
|
|
});
|
|
await expect.poll(renderedCounts).toMatchObject({ dots: 70, reservoir: 1, pump: 1, tank: 1, waterQuality: 1 });
|
|
const overview = await renderedCounts();
|
|
expect(overview.symbols).toBeLessThan(overview.dots / 3);
|
|
await page.screenshot({ path: testInfo.outputPath("dense-overview.png") });
|
|
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 10.99 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ waterQuality: 0, waterQualityHit: 0, pressure: 0, gisFlow: 0, gisFlowHits: 0 });
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 11 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ waterQuality: 1, waterQualityHit: 1, pressure: 1, gisFlow: 1, gisFlowHits: 1 });
|
|
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 11.99 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ nodes: 0, nodeHits: 0, gisDetails: 0, gisDetailHits: 0 });
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 12 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ nodes: 1, nodeHits: 1, gisDetails: 1, gisDetailHits: 1 });
|
|
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 12.9 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ dots: 0, symbols: 0, hits: 0, waterQuality: 1 });
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 13 }));
|
|
await expect.poll(renderedCounts).toMatchObject({ dots: 70, hits: 70 });
|
|
|
|
await page.evaluate(() => window.__waterNetworkMap!.jumpTo({ zoom: 15.5 }));
|
|
await expect.poll(async () => (await renderedCounts()).symbols).toBeGreaterThan(overview.symbols);
|
|
await page.screenshot({ path: testInfo.outputPath("dense-detail.png") });
|
|
|
|
await page.evaluate(() => {
|
|
const map = window.__waterNetworkMap!;
|
|
map.setFeatureState({ source: "density-fixture", id: "valve-35" }, { selected: true });
|
|
map.jumpTo({ zoom: 13.1 });
|
|
});
|
|
await expect.poll(() => page.evaluate(() => window.__waterNetworkMap!
|
|
.getFeatureState({ source: "density-fixture", id: "valve-35" }).selected)).toBe(true);
|
|
await page.screenshot({ path: testInfo.outputPath("dense-selected.png") });
|
|
});
|
|
}
|