Hinweise optimiert
This commit is contained in:
@@ -98,8 +98,6 @@ function route(id: string, coordinates: [number, number][]): RouteResult {
|
||||
distanceNm: 1,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["Test"],
|
||||
routingMode: "fairway"
|
||||
};
|
||||
|
||||
@@ -80,4 +80,52 @@ describe("CourseAssistantPanel", () => {
|
||||
expect(screen.getByText(/GPS-Fix ist veraltet/)).toBeVisible();
|
||||
expect(screen.getByText("---")).toBeVisible();
|
||||
});
|
||||
|
||||
it("keeps every open route warning visible while guidance is active", () => {
|
||||
render(
|
||||
<CourseAssistantPanel
|
||||
guidance={null}
|
||||
gpsStatus="tracking"
|
||||
headingDeg={null}
|
||||
headingSource="--"
|
||||
accuracyM={null}
|
||||
fixStale={false}
|
||||
routeWarnings={[
|
||||
{
|
||||
code: "ROUTING_SOURCE",
|
||||
severity: "info",
|
||||
message: "Routingquelle: lokaler Graph."
|
||||
},
|
||||
{
|
||||
code: "BRIDGE_TOO_LOW",
|
||||
severity: "caution",
|
||||
message: "Eine Brücke ist zu niedrig."
|
||||
},
|
||||
{
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Die Snap-Suche wurde begrenzt."
|
||||
},
|
||||
{
|
||||
code: "START_SNAP_TOO_FAR",
|
||||
severity: "caution",
|
||||
message: "Der Startpunkt liegt weit vom Fahrwasser entfernt."
|
||||
},
|
||||
{
|
||||
code: "DESTINATION_SNAP_TOO_FAR",
|
||||
severity: "caution",
|
||||
message: "Der Zielpunkt liegt weit vom Fahrwasser entfernt."
|
||||
}
|
||||
]}
|
||||
onStop={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Hinweise (4)")).toBeVisible();
|
||||
expect(screen.getByText("Eine Brücke ist zu niedrig.")).toBeVisible();
|
||||
expect(screen.getByText("Die Snap-Suche wurde begrenzt.")).toBeVisible();
|
||||
expect(screen.getByText("Der Startpunkt liegt weit vom Fahrwasser entfernt.")).toBeVisible();
|
||||
expect(screen.getByText("Der Zielpunkt liegt weit vom Fahrwasser entfernt.")).toBeVisible();
|
||||
expect(screen.getByText("Routingdetails (1)")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -128,8 +128,6 @@ test("starts a visible dynamic course assistant only after route planning", asyn
|
||||
distanceNm: 0.7,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["E2E"],
|
||||
routingMode: "fairway"
|
||||
})
|
||||
|
||||
@@ -35,6 +35,33 @@ describe("GPX export", () => {
|
||||
geometry: { type: "LineString", coordinates: [[7, 53], [181, 52]] }
|
||||
})).toThrow(/ungültige Koordinaten/);
|
||||
});
|
||||
|
||||
it("does not export retired route warnings", () => {
|
||||
const gpx = createRouteGpx({
|
||||
...routeFixture,
|
||||
warnings: [
|
||||
{
|
||||
code: "FAIRWAY_ROUTE",
|
||||
severity: "info",
|
||||
message: "Alter Graph-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "NO_KNOWN_DEPTH",
|
||||
severity: "caution",
|
||||
message: "Alter Tiefen-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Aktueller Such-Hinweis"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(gpx).not.toContain("Alter Graph-Hinweis");
|
||||
expect(gpx).not.toContain("Alter Tiefen-Hinweis");
|
||||
expect(gpx).toContain("Aktueller Such-Hinweis");
|
||||
});
|
||||
});
|
||||
|
||||
const routeFixture: RouteResult = {
|
||||
@@ -47,8 +74,6 @@ const routeFixture: RouteResult = {
|
||||
distanceNm: 153.69,
|
||||
eta: null,
|
||||
warnings: [{ code: "NOT_OFFICIAL", severity: "caution", message: "Nicht amtlich & vor Ort prüfen" }],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["OpenStreetMap <curated>"],
|
||||
routingMode: "fairway"
|
||||
};
|
||||
|
||||
@@ -530,8 +530,6 @@ describe("MapView marine feature information", () => {
|
||||
distanceNm: 120,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: [],
|
||||
routingMode: "fairway"
|
||||
};
|
||||
|
||||
@@ -22,9 +22,9 @@ describe("NavigationToolRail", () => {
|
||||
anchor: "alarm",
|
||||
conditions: "active",
|
||||
upcoming: "caution",
|
||||
route: "stale"
|
||||
route: "caution"
|
||||
}}
|
||||
badges={{ upcoming: 123 }}
|
||||
badges={{ upcoming: 123, route: 4 }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -58,7 +58,10 @@ describe("NavigationToolRail", () => {
|
||||
expect(upcoming).toHaveAttribute("data-status", "caution");
|
||||
expect(within(upcoming).getByText("99+")).toBeVisible();
|
||||
|
||||
const route = screen.getByRole("button", { name: /Route, Daten veraltet, öffnen/ });
|
||||
const route = screen.getByRole("button", {
|
||||
name: /Route, Warnung, 4 Hinweise, öffnen/
|
||||
});
|
||||
expect(within(route).getByText("4")).toBeVisible();
|
||||
fireEvent.click(route);
|
||||
expect(onSelect).toHaveBeenCalledWith("route");
|
||||
});
|
||||
|
||||
@@ -68,6 +68,71 @@ describe("offline voyage storage", () => {
|
||||
expect(listOfflineVoyages(localStorage)).toEqual([]);
|
||||
});
|
||||
|
||||
it("removes retired warnings and depth fields from legacy offline routes", () => {
|
||||
const record = createOfflineVoyageRecord(
|
||||
{ route: routeFixture },
|
||||
{ id: "legacy-voyage", savedAt: "2026-07-19T12:00:00.000Z" }
|
||||
);
|
||||
const legacyRecord = {
|
||||
...record,
|
||||
route: {
|
||||
...record.route,
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
warnings: [
|
||||
{
|
||||
code: "FAIRWAY_ROUTE",
|
||||
severity: "info",
|
||||
message: "Alter Graph-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "FAIRWAY_DATA_NOT_OFFICIAL",
|
||||
severity: "caution",
|
||||
message: "Alter Daten-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "DEPTH_UNKNOWN",
|
||||
severity: "caution",
|
||||
message: "Alter Tiefenpunkt-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "DEPTH_PARTIAL",
|
||||
severity: "caution",
|
||||
message: "Alter Teil-Tiefenhinweis"
|
||||
},
|
||||
{
|
||||
code: "NO_KNOWN_DEPTH",
|
||||
severity: "caution",
|
||||
message: "Alter Unbekannt-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "DEPTH_TOO_SHALLOW",
|
||||
severity: "critical",
|
||||
message: "Alter Flachwasser-Hinweis"
|
||||
},
|
||||
{
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Suche begrenzt"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
localStorage.setItem(OFFLINE_VOYAGES_STORAGE_KEY, JSON.stringify([legacyRecord]));
|
||||
|
||||
const restored = loadOfflineVoyage("legacy-voyage", localStorage);
|
||||
|
||||
expect(restored?.route.warnings).toEqual([
|
||||
{
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Suche begrenzt"
|
||||
}
|
||||
]);
|
||||
expect(restored?.route).not.toHaveProperty("minKnownDepthM");
|
||||
expect(restored?.route).not.toHaveProperty("unknownDepthRatio");
|
||||
});
|
||||
|
||||
it("rejects unsafe route coordinates before writing", () => {
|
||||
expect(() => createOfflineVoyageRecord({
|
||||
route: {
|
||||
@@ -91,8 +156,6 @@ const routeFixture: RouteResult = {
|
||||
durationMinutes: 120,
|
||||
eta: "2026-07-20T08:00:00.000Z",
|
||||
warnings: [{ code: "NOT_OFFICIAL", severity: "caution", message: "Nicht amtlich" }],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["OSM"],
|
||||
routingMode: "fairway",
|
||||
routeSnaps: {
|
||||
@@ -115,8 +178,6 @@ const routeFixture: RouteResult = {
|
||||
distanceNm: 160,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["OSM"]
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -22,8 +22,6 @@ const route: RouteResult = {
|
||||
distanceNm: 60,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["test"]
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { act, cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { RoutePlanner } from "../src/components/RoutePlanner";
|
||||
import { DEFAULT_BOAT_PROFILE } from "../src/lib/boat-profile";
|
||||
@@ -416,6 +416,7 @@ describe("RoutePlanner", () => {
|
||||
);
|
||||
|
||||
expect(screen.getByText("Routenergebnis")).toBeVisible();
|
||||
expect(screen.getByText("Hinweise (2)")).toBeVisible();
|
||||
expect(screen.getByText("Brücke ist zu niedrig.")).toBeVisible();
|
||||
expect(screen.getByText("Wind aufmerksam beobachten.")).toBeVisible();
|
||||
expect(screen.queryByRole("button", { name: "Start auf Karte setzen" })).not.toBeInTheDocument();
|
||||
@@ -426,6 +427,89 @@ describe("RoutePlanner", () => {
|
||||
expect(screen.getByRole("button", { name: "Zur Route" })).toBeVisible();
|
||||
});
|
||||
|
||||
it("filters retired fairway and depth warnings from route results", () => {
|
||||
const result = {
|
||||
...routeOption("primary", "Hauptroute", 24.48),
|
||||
warnings: [
|
||||
{
|
||||
code: "FAIRWAY_ROUTE",
|
||||
severity: "info" as const,
|
||||
message: "Entfernter Graph-Hinweis."
|
||||
},
|
||||
{
|
||||
code: "FAIRWAY_DATA_NOT_OFFICIAL",
|
||||
severity: "caution" as const,
|
||||
message: "Entfernter Daten-Hinweis."
|
||||
},
|
||||
{
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution" as const,
|
||||
message: "Die Snap-Suche wurde begrenzt."
|
||||
},
|
||||
{
|
||||
code: "DEPTH_PARTIAL",
|
||||
severity: "caution" as const,
|
||||
message: "Entfernter Teil-Tiefenhinweis."
|
||||
},
|
||||
{
|
||||
code: "NO_KNOWN_DEPTH",
|
||||
severity: "caution" as const,
|
||||
message: "Entfernter Unbekannt-Hinweis."
|
||||
},
|
||||
{
|
||||
code: "DEPTH_UNKNOWN",
|
||||
severity: "caution" as const,
|
||||
message: "Entfernter Tiefenpunkt-Hinweis."
|
||||
},
|
||||
{
|
||||
code: "DEPTH_TOO_SHALLOW",
|
||||
severity: "critical" as const,
|
||||
message: "Entfernter Flachwasser-Hinweis."
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
render(
|
||||
<RoutePlanner
|
||||
startPoint={{ lat: 53.4647, lon: 7.474 }}
|
||||
gpsPosition={null}
|
||||
destination={{ lat: 53.5147, lon: 8.1203 }}
|
||||
result={result}
|
||||
routeOptions={[result]}
|
||||
weatherReport={null}
|
||||
weatherLoading={false}
|
||||
weatherError={null}
|
||||
loading={false}
|
||||
error={null}
|
||||
pickMode={null}
|
||||
onSubmit={vi.fn()}
|
||||
onPickStart={vi.fn()}
|
||||
onPickDestination={vi.fn()}
|
||||
onClearStart={vi.fn()}
|
||||
onClearDestination={vi.fn()}
|
||||
onUseGpsAsStart={vi.fn()}
|
||||
onSelectRoute={vi.fn()}
|
||||
onCollapse={vi.fn()}
|
||||
embedded
|
||||
/>
|
||||
);
|
||||
|
||||
const warnings = screen.getByRole("region", { name: "Routing-Hinweise" });
|
||||
expect(within(warnings).getByText("Hinweise (1)")).toBeVisible();
|
||||
expect(within(warnings).getByText("Die Snap-Suche wurde begrenzt.")).toBeVisible();
|
||||
expect(screen.queryByText("Entfernter Graph-Hinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Entfernter Daten-Hinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Entfernter Teil-Tiefenhinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Entfernter Unbekannt-Hinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Entfernter Tiefenpunkt-Hinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Entfernter Flachwasser-Hinweis.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Routingdetails/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Hauptroute").closest(".route-result")).toHaveAttribute(
|
||||
"data-severity",
|
||||
"caution"
|
||||
);
|
||||
});
|
||||
|
||||
it("shows the route weather report after a route was calculated", () => {
|
||||
render(
|
||||
<RoutePlanner
|
||||
@@ -437,8 +521,6 @@ describe("RoutePlanner", () => {
|
||||
distanceNm: 4.2,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: [],
|
||||
routingMode: "fairway"
|
||||
}}
|
||||
@@ -608,8 +690,6 @@ function routeOption(id: string, name: string, distanceNm: number) {
|
||||
distanceNm,
|
||||
eta: "2026-07-13T14:00:00.000Z",
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: [],
|
||||
routingMode: "fairway" as const
|
||||
};
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { RouteWarning } from "@watermaps/shared";
|
||||
import {
|
||||
isOpenRouteWarning,
|
||||
isRetainedRouteWarning
|
||||
} from "../src/lib/route-warnings";
|
||||
|
||||
describe("route warning filters", () => {
|
||||
it("filters every retired fairway and depth warning code centrally", () => {
|
||||
const retiredCodes = [
|
||||
"FAIRWAY_ROUTE",
|
||||
"FAIRWAY_DATA_NOT_OFFICIAL",
|
||||
"DEPTH_UNKNOWN",
|
||||
"DEPTH_PARTIAL",
|
||||
"NO_KNOWN_DEPTH",
|
||||
"DEPTH_TOO_SHALLOW"
|
||||
];
|
||||
|
||||
for (const code of retiredCodes) {
|
||||
const warning: RouteWarning = {
|
||||
code,
|
||||
severity: "critical",
|
||||
message: `Alter Hinweis ${code}`
|
||||
};
|
||||
expect(isRetainedRouteWarning(warning)).toBe(false);
|
||||
expect(isOpenRouteWarning(warning)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("retains current warnings and only counts non-informational ones as open", () => {
|
||||
expect(
|
||||
isRetainedRouteWarning({
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Suche begrenzt"
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
isOpenRouteWarning({
|
||||
code: "ROUTE_SEARCH_LIMITED",
|
||||
severity: "caution",
|
||||
message: "Suche begrenzt"
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
isOpenRouteWarning({
|
||||
code: "ROUTING_SOURCE",
|
||||
severity: "info",
|
||||
message: "Routingquelle"
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -148,8 +148,6 @@ const routeFixture: RouteResult = {
|
||||
distanceNm: 25,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: [],
|
||||
routingMode: "fairway"
|
||||
};
|
||||
|
||||
@@ -126,8 +126,6 @@ const routeFixture: RouteResult = {
|
||||
distanceNm: 0.4,
|
||||
eta: null,
|
||||
warnings: [],
|
||||
minKnownDepthM: null,
|
||||
unknownDepthRatio: 1,
|
||||
dataSources: ["Test"],
|
||||
routingMode: "fairway"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user