From 5c47498472b7e1d11b1edb642df53cdbb00fa2b1 Mon Sep 17 00:00:00 2001 From: gnezim Date: Mon, 20 Apr 2026 08:24:01 +0300 Subject: [PATCH] Allow non-null assertions in tests; refactor two production hotspots to drop them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - eslint.config.js: disable no-non-null-assertion for *.test.ts/tsx and tests/** (fixture-driven tests routinely use arr[0]! after a length check — signal there is low). - closestFlight.ts: replace flights.legs[0]! / flights[flights.length-1]! with explicit null checks. - FlightDetailsAccordion.tsx: refactor transition + meal/service branches to use local consts narrowed by a preceding truthy check, dropping the `leg.transition!.registration!` patterns. Warning count: 190 → 65. Remaining warnings are pre-existing production-code non-null assertions spread across the codebase. --- eslint.config.js | 12 +++++++ src/features/online-board/closestFlight.ts | 23 +++++++++---- .../details-panels/FlightDetailsAccordion.tsx | 34 ++++++++++--------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 0ed91494..34fd74e5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -177,4 +177,16 @@ export default [ ], }, }, + // Test files get a looser ruleset: non-null assertions are idiomatic + // when fixtures guarantee presence (`arr[0]!` after an explicit length + // check). Production code keeps the warning; tests don't. + { + files: [ + "src/**/*.test.{ts,tsx}", + "tests/**/*.{ts,tsx}", + ], + rules: { + "@typescript-eslint/no-non-null-assertion": "off", + }, + }, ]; diff --git a/src/features/online-board/closestFlight.ts b/src/features/online-board/closestFlight.ts index 3cfb9df9..ed509c59 100644 --- a/src/features/online-board/closestFlight.ts +++ b/src/features/online-board/closestFlight.ts @@ -11,13 +11,17 @@ import type { ISimpleFlight, IFlightLeg } from "./types.js"; function firstLeg(flight: ISimpleFlight): IFlightLeg { - return flight.routeType === "Direct" ? flight.leg : flight.legs[0]!; + if (flight.routeType === "Direct") return flight.leg; + const first = flight.legs[0]; + if (!first) throw new Error("MultiLeg flight has no legs"); + return first; } function lastLeg(flight: ISimpleFlight): IFlightLeg { - return flight.routeType === "Direct" - ? flight.leg - : flight.legs[flight.legs.length - 1]!; + if (flight.routeType === "Direct") return flight.leg; + const last = flight.legs[flight.legs.length - 1]; + if (!last) throw new Error("MultiLeg flight has no legs"); + return last; } function pickTime(flight: ISimpleFlight, isArrival: boolean): string | null { @@ -78,9 +82,14 @@ export function findClosestFlightId( if (!isTodayYyyymmdd(opts.searchDate, now)) { // For future days, show the first flight; for past days, the last. - const firstMs = parseTimeMs(pickTime(flights[0]!, opts.isArrival)); - if (firstMs !== null && nowMs < firstMs) return flights[0]!.id; - return flights[flights.length - 1]!.id; + // We've already short-circuited on `!flights.length` above so both + // ends are defined. + const first = flights[0]; + const last = flights[flights.length - 1]; + if (!first || !last) return null; + const firstMs = parseTimeMs(pickTime(first, opts.isArrival)); + if (firstMs !== null && nowMs < firstMs) return first.id; + return last.id; } let best: string | null = flights[0]?.id ?? null; diff --git a/src/features/online-board/components/details-panels/FlightDetailsAccordion.tsx b/src/features/online-board/components/details-panels/FlightDetailsAccordion.tsx index 2b780308..3e74efcd 100644 --- a/src/features/online-board/components/details-panels/FlightDetailsAccordion.tsx +++ b/src/features/online-board/components/details-panels/FlightDetailsAccordion.tsx @@ -140,38 +140,38 @@ export const FlightDetailsAccordion: FC = ({ leg, v const rows: RowDef[] = []; - if (shouldShowTransition(leg.transition?.registration, leg.status, viewType)) { - const item = leg.transition!.registration!; + const registration = leg.transition?.registration; + if (registration && shouldShowTransition(registration, leg.status, viewType)) { rows.push({ id: "registration", icon: ICON_REGISTRATION, title: t("DETAILS.REGISTRATION"), - statusStatus: item.status, - body: , + statusStatus: registration.status, + body: , legacyTestId: "registration-panel", isTransition: true, }); } - if (shouldShowTransition(leg.transition?.boarding, leg.status, viewType)) { - const item = leg.transition!.boarding!; + const boarding = leg.transition?.boarding; + if (boarding && shouldShowTransition(boarding, leg.status, viewType)) { rows.push({ id: "boarding", icon: ICON_BOARDING, title: t("DETAILS.BOARDING"), - statusStatus: item.status, - body: , + statusStatus: boarding.status, + body: , legacyTestId: "boarding-panel", isTransition: true, }); } - if (shouldShowTransition(leg.transition?.deboarding, leg.status, viewType)) { - const item = leg.transition!.deboarding!; + const deboarding = leg.transition?.deboarding; + if (deboarding && shouldShowTransition(deboarding, leg.status, viewType)) { rows.push({ id: "deboarding", icon: ICON_DEBOARDING, title: t("DETAILS.DEBOARDING"), - statusStatus: item.status, - body: , + statusStatus: deboarding.status, + body: , legacyTestId: "deboarding-panel", isTransition: true, }); @@ -187,20 +187,22 @@ export const FlightDetailsAccordion: FC = ({ leg, v body: , }); } - if ((leg.equipment.meal?.length ?? 0) > 0) { + const meals = leg.equipment.meal; + if (meals && meals.length > 0) { rows.push({ id: "meal", icon: ICON_MEAL, title: t("SHARED.FOOD"), - body: , + body: , }); } - if ((leg.equipment.aircraft?.actual?.onBoardServices?.length ?? 0) > 0) { + const onBoardServices = leg.equipment.aircraft?.actual?.onBoardServices; + if (onBoardServices && onBoardServices.length > 0) { rows.push({ id: "services", icon: ICON_SERVICES, title: t("DETAILS.ON_BOARD_SERVICES"), - body: , + body: , }); }