Ignore malformed direct map routes

This commit is contained in:
2026-05-21 19:22:23 +03:00
parent 19dbdc5127
commit ca9978f003
3 changed files with 81 additions and 1 deletions
@@ -476,6 +476,28 @@ describe("FlightsMapStartPage — C.4 integration", () => {
expect(popups[1]!.content).toContain("https://www.aeroflot.ru/sb/app/ru-ru");
});
it("does not draw malformed multi-hop direct routes when transfers are off", () => {
searchState.routes = [
{ route: ["SVO", "LED"], isDirect: true },
{ route: ["SVO", "AER", "LED"], isDirect: true },
];
setMapFilter({
departure: "MOW",
arrival: "LED",
date: null,
showInternal: false,
showInternational: false,
showTransfers: false,
});
render(<FlightsMapStartPage />);
const polylines = lastMapCanvasProps!["polylines"] as Array<{ cityIds: string[] }>;
expect(polylines).toEqual([
expect.objectContaining({ cityIds: ["MOW", "LED"] }),
]);
});
it("does not render popups in spider mode (departure only)", () => {
searchState.routes = [{ route: ["MOW", "LED"], isDirect: true }];
render(<FlightsMapStartPage />);
@@ -102,6 +102,49 @@ describe("filterRoutes — connections", () => {
});
});
describe("filterRoutes — route mode without transfer-only toggle", () => {
it("drops malformed direct routes that contain intermediate cities", () => {
const routes: IFlightRoute[] = [
{ route: ["SVO", "LED"], isDirect: true },
{ route: ["SVO", "AER", "LED"], isDirect: true },
{ route: ["SVO", "AER", "LED"], isDirect: false },
];
const out = filterRoutes(
routes,
filter({ departure: "MOW", arrival: "LED", connections: false }),
d,
);
expect(out).toEqual([{ route: ["SVO", "LED"], isDirect: true }]);
});
it("keeps connecting routes in route mode when transfer-only toggle is active", () => {
const routes: IFlightRoute[] = [
{ route: ["SVO", "LED"], isDirect: true },
{ route: ["SVO", "AER", "LED"], isDirect: false },
];
const out = filterRoutes(
routes,
filter({ departure: "MOW", arrival: "LED", connections: true }),
d,
);
expect(out).toEqual([{ route: ["SVO", "AER", "LED"], isDirect: false }]);
});
it("does not apply endpoint-direct filtering in spider mode", () => {
const routes: IFlightRoute[] = [
{ route: ["SVO", "AER", "LED"], isDirect: true },
];
const out = filterRoutes(routes, filter({ departure: "MOW" }), d);
expect(out).toEqual(routes);
});
});
describe("filterRoutes — airport-code normalization", () => {
it("treats airport codes (SVO, JFK) as their city codes (MOW, NYC)", () => {
const routes: IFlightRoute[] = [
+16 -1
View File
@@ -17,7 +17,10 @@ import type { IFlightRoute, IFlightsMapFilterState } from "./types.js";
export function filterRoutes(
routes: IFlightRoute[],
filter: Pick<IFlightsMapFilterState, "domestic" | "international" | "connections">,
filter: Pick<
IFlightsMapFilterState,
"departure" | "arrival" | "domestic" | "international" | "connections"
>,
dictionaries: IDictionaries,
): IFlightRoute[] {
const { domestic, international, connections } = filter;
@@ -32,6 +35,17 @@ export function filterRoutes(
r.route.some((code) => dictionaries.otherCityCodes.has(toCityCode(code)));
const hasConnections = (r: IFlightRoute): boolean => !r.isDirect;
const isEndpointDirectRoute = (r: IFlightRoute): boolean => {
if (!filter.departure || !filter.arrival) return true;
if (!r.isDirect) return false;
const normalized = r.route.map(toCityCode);
return (
normalized.length === 2 &&
normalized[0] === toCityCode(filter.departure) &&
normalized[1] === toCityCode(filter.arrival)
);
};
const predicates: Array<(r: IFlightRoute) => boolean> = [];
@@ -39,6 +53,7 @@ export function filterRoutes(
else if (international && !domestic) predicates.push(isInternational);
if (connections) predicates.push(hasConnections);
else if (filter.arrival) predicates.push(isEndpointDirectRoute);
if (predicates.length === 0) return routes;
return routes.filter((r) => predicates.every((p) => p(r)));