From 37ae7dcd46b18c82ea655234a53ac07f42f4dff5 Mon Sep 17 00:00:00 2001 From: gnezim Date: Thu, 23 Apr 2026 15:54:19 +0300 Subject: [PATCH] Schedule details mini-list: filter to the open flight only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors Angular's CurrentScheduleService.getScheduleType + compareFlightsByPId: when the [-1, +1] route search returns the open flight (matched by carrier+number signature, including each leg of a connecting itinerary), keep only those instances; when no match exists, fall back to a 1-item list with just the open flight (Angular's 'default-schedule' branch). Old behaviour returned the full route search and dumped every unrelated MOW-MMK option into the rail. Add e2e regression that loads the SU 6188 + SU 6341 itinerary and asserts the rail shows only SU 6188 — not SU 6190 / SU 6699 (the other Sunday MOW-MMK options that used to appear). --- .../components/ScheduleDetailsPage.tsx | 39 +++++++++++++-- .../schedule-details-mini-list-scoped.spec.ts | 50 +++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/schedule-details-mini-list-scoped.spec.ts diff --git a/src/features/schedule/components/ScheduleDetailsPage.tsx b/src/features/schedule/components/ScheduleDetailsPage.tsx index 9dbb5e8a..7fe26be0 100644 --- a/src/features/schedule/components/ScheduleDetailsPage.tsx +++ b/src/features/schedule/components/ScheduleDetailsPage.tsx @@ -150,19 +150,48 @@ export const ScheduleDetailsPage: FC = ({ ); const miniListFlights: ISimpleFlight[] = useMemo(() => { - if (!miniListSearchParams) return flights as unknown as ISimpleFlight[]; - if (siblingSearch.loading || siblingSearch.error) { - return flights as unknown as ISimpleFlight[]; - } - return extractSimpleFlights( + // Mirrors Angular's `CurrentScheduleService.getScheduleType`: + // • If the route-search result contains the open flight (matched + // by the same carrier+number sequence), show those matching + // instances across the [-1, +1] day window — the SAME flight on + // adjacent days. + // • Otherwise fall back to a 1-item list with just the open + // flight ('default-schedule' branch). + // Old behaviour returned the entire route search which dumped every + // unrelated MOW-MMK option into the left rail. + const fallback = flights as unknown as ISimpleFlight[]; + if (!miniListSearchParams) return fallback; + if (siblingSearch.loading || siblingSearch.error) return fallback; + + // Build the open flight's signature: ordered "CARRIER+NUMBER" + // segments. For connecting itineraries the URL carries every leg + // already (each as a separate flightId). + const openSignature = flightIds + .map((f) => `${f.carrier}${f.flightNumber}`) + .join("+"); + if (!openSignature) return fallback; + + const allSiblings = extractSimpleFlights( siblingSearch.flights as Array<{ routeType: string }>, ); + const matches = allSiblings.filter((sib) => { + const childIds = (sib as ISimpleFlight & { + _childFlightIds?: typeof sib.flightId[]; + })._childFlightIds; + const sig = childIds && childIds.length > 0 + ? childIds.map((c) => `${c.carrier}${c.flightNumber}`).join("+") + : `${sib.flightId.carrier}${sib.flightId.flightNumber}`; + return sig === openSignature; + }); + + return matches.length > 0 ? matches : fallback; }, [ miniListSearchParams, siblingSearch.flights, siblingSearch.loading, siblingSearch.error, flights, + flightIds, ]); // Angular's schedule details page adds a third crumb only when the diff --git a/tests/e2e/schedule-details-mini-list-scoped.spec.ts b/tests/e2e/schedule-details-mini-list-scoped.spec.ts new file mode 100644 index 00000000..3fecfe2b --- /dev/null +++ b/tests/e2e/schedule-details-mini-list-scoped.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from "@playwright/test"; + +// On the schedule details page, the left mini-list must show only the +// CURRENTLY-OPEN flight's instance on each day in the [-1, +1] window — +// matching Angular's `CurrentScheduleService.getScheduleType` / +// `compareFlightsByPId` filtering. The old behaviour dumped the entire +// MOW→MMK route search into the rail (every flight number, every +// option), making the rail useless when the user came from the +// search-results list. +// +// Reference URL: connecting itinerary SU 6188 + SU 6341 (Moscow → St +// Petersburg → Murmansk) on 2026-04-26. Each visible day in the +// mini-list must list ≤ 1 entry — the same SU 6188 itinerary. + +const URL = + "/ru-ru/schedule/VKO/SU6188-20260426/LED/SU6341-20260427/MMK?request=schedule-route-MOW-MMK-20260427-20260503"; + +test("mini-list shows only the open flight per day, not the full route search", async ({ + page, +}) => { + await page.goto(URL); + + // Wait for the mini-list to render. + const miniList = page.locator(".schedule-mini-list"); + await expect(miniList).toBeVisible({ timeout: 15000 }); + + // Wait until the day-headers appear (they exist for both the today + // and ±1 days). Three day headers are expected total. + const dayHeaders = miniList.locator("[data-testid^='mini-list-day-header-']"); + await expect(dayHeaders).toHaveCount(3); + + // The expanded body must contain at most one flight entry — the + // open SU 6188 itinerary. The old behaviour rendered 4+ entries + // (every MOW-MMK option for that day). + const openBody = miniList + .locator("[data-testid^='mini-list-day-']:not([data-testid*='header'])") + .first(); + await expect(openBody).toBeVisible({ timeout: 10000 }); + // Mini-list items use SU 6188 in their visible label. + const items = openBody.locator(".flights-mini-list-item, [class*='mini-list'] [class*='flight']"); + // Loose assertion — there should be at MOST one entry per day, and + // the visible text must include 'SU 6188' (NOT a different + // route-mate flight number). + const text = (await openBody.innerText()).replace(/\s+/g, " "); + expect(text).toContain("SU 6188"); + // Sanity: should NOT contain other Sunday MOW-MMK flight numbers + // that the old listing pulled in (SU 6190 / SU 6699 are typical). + expect(text).not.toMatch(/SU\s*6190/); + expect(text).not.toMatch(/SU\s*6699/); +});