diff --git a/src/components/StationTimeTableRow.tsx b/src/components/StationTimeTableRow.tsx index ca5c28f7..8aad0935 100644 --- a/src/components/StationTimeTableRow.tsx +++ b/src/components/StationTimeTableRow.tsx @@ -8,7 +8,7 @@ import { TableCell, TableRow, } from '@mui/material'; -import { ChevronRight } from 'mdi-material-ui'; +import { Airplane, ChevronRight } from 'mdi-material-ui'; import RouterLink from 'next/link'; import { @@ -47,7 +47,7 @@ function StationTimeTableRow({ : train.trainType.name + train.trainNumber; const deptOrDestStation = timeTableType === TimeTableRowType.Departure - ? getTrainDestinationStation(train) + ? getTrainDestinationStation(train, stationCode) : getTrainDepartureStation(train); const deptOrDestStationName = deptOrDestStation ? getTrainStationName(deptOrDestStation) @@ -111,6 +111,9 @@ function StationTimeTableRow({ onClick={handleStationClick} > {deptOrDestStationName} + {deptOrDestStation?.shortCode === 'LEN' && ( + + )} diff --git a/src/utils/__tests__/train.test.ts b/src/utils/__tests__/train.test.ts index 70b7e7b8..60854e6c 100644 --- a/src/utils/__tests__/train.test.ts +++ b/src/utils/__tests__/train.test.ts @@ -106,6 +106,80 @@ const train: TrainDetailsFragment = { ], }; +/** + * Ring rail (Kehärata) train HKI to HKI via LEN (airport) + */ +const ringRailTrainBase: TrainDetailsFragment = { + ...trainBase, + commuterLineid: 'I', + timeTableRows: [ + { + ...departureTimeTableRowBase, + scheduledTime: '2023-01-25T08:55:00Z', + station: { + name: 'Helsinki', + shortCode: 'HKI', + }, + }, + { + ...arrivalTimeTableRowBase, + scheduledTime: '2023-01-25T09:00:00Z', + station: { + name: 'Pasila', + shortCode: 'PSL', + }, + }, + { + ...departureTimeTableRowBase, + scheduledTime: '2023-01-25T09:05:00Z', + station: { + name: 'Pasila', + shortCode: 'PSL', + }, + }, + { + ...arrivalTimeTableRowBase, + scheduledTime: '2023-01-25T11:00:00Z', + station: { + name: 'Lentoasema', + shortCode: 'LEN', + }, + }, + { + ...departureTimeTableRowBase, + scheduledTime: '2023-01-25T11:05:00Z', + station: { + name: 'Lentoasema', + shortCode: 'LEN', + }, + }, + { + ...arrivalTimeTableRowBase, + scheduledTime: '2023-01-25T11:30:00Z', + station: { + name: 'Pasila', + shortCode: 'PSL', + }, + }, + { + ...departureTimeTableRowBase, + scheduledTime: '2023-01-25T11:35:00Z', + station: { + name: 'Pasila', + shortCode: 'PSL', + }, + }, + { + ...arrivalTimeTableRowBase, + scheduledTime: '2023-01-25T11:40:00Z', + station: { + name: 'Helsinki', + shortCode: 'HKI', + }, + }, + ], +}; + describe('getTimeTableRowRealTime', () => { it('should be the scheduled time of the row when neither actual time or live estimate time is defined', () => { expect( @@ -177,6 +251,50 @@ describe('getTrainDestinationStation', () => { expect(station).toBeDefined(); expect(station!.name).toBe('Tampere'); }); + describe.each(['I', 'P'])('ring rail %s train', (commuterLineid: string) => { + const ringRailTrain = { + ...ringRailTrainBase, + commuterLineid: commuterLineid, + }; + + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should be the LEN (airport) station when the given station is earlier than LEN', () => { + jest.setSystemTime(parseISO('2023-01-25T08:55:00Z')); + + const destStationAtHki = getTrainDestinationStation(ringRailTrain, 'HKI'); + expect(destStationAtHki).toBeDefined(); + expect(destStationAtHki!.name).toBe('Lentoasema'); + + const destStationAtPsl = getTrainDestinationStation(ringRailTrain, 'PSL'); + expect(destStationAtPsl).toBeDefined(); + expect(destStationAtPsl!.name).toBe('Lentoasema'); + }); + + it('should be the station of the destination time table row when the given station is later than LEN', () => { + // Train is between section PSL - LEN based on current time and train schedule + // So train has already passed PSL (first time) + jest.setSystemTime(parseISO('2023-01-25T10:30:00Z')); + + const destStationAtLen = getTrainDestinationStation(ringRailTrain, 'LEN'); + expect(destStationAtLen).toBeDefined(); + expect(destStationAtLen!.name).toBe('Helsinki'); + + const destStationAtPsl = getTrainDestinationStation(ringRailTrain, 'PSL'); + expect(destStationAtPsl).toBeDefined(); + expect(destStationAtPsl!.name).toBe('Helsinki'); + + const destStationAtHki = getTrainDestinationStation(ringRailTrain, 'HKI'); + expect(destStationAtHki).toBeDefined(); + expect(destStationAtHki!.name).toBe('Helsinki'); + }); + }); }); describe('getTrainDepartureStationName', () => { diff --git a/src/utils/train.ts b/src/utils/train.ts index a94a74d7..9f96e8ed 100644 --- a/src/utils/train.ts +++ b/src/utils/train.ts @@ -1,7 +1,12 @@ import { parseISO } from 'date-fns'; import { orderBy } from 'lodash'; -import { TrainByStationFragment } from '../graphql/generated/digitraffic'; +import { + TimeTableRowType, + TrainByStationFragment, +} from '../graphql/generated/digitraffic'; + +import getTimeTableRowForStation from './getTimeTableRowForStation'; export function getTimeTableRowRealTime(row: { scheduledTime: string; @@ -30,7 +35,37 @@ export function getTrainDepartureStation(train: TrainByStationFragment) { return getDepartureTimeTableRow(train)?.station; } -export function getTrainDestinationStation(train: TrainByStationFragment) { +export function getTrainDestinationStation( + train: TrainByStationFragment, + stationCode?: string +) { + // Special handling for ring route (kehärata) trains: + // Return LEN (airport) as the destination station if + // the given station code is earlier on the train + // time table rows than the airport. + if ( + stationCode && + train.commuterLineid && + ['I', 'P'].includes(train.commuterLineid) + ) { + const stationRow = getTimeTableRowForStation( + stationCode, + train, + TimeTableRowType.Departure + ); + const airportArrivalRow = getTimeTableRowForStation( + 'LEN', + train, + TimeTableRowType.Arrival + ); + if ( + airportArrivalRow && + stationRow && + stationRow.scheduledTime < airportArrivalRow.scheduledTime + ) { + return airportArrivalRow.station; + } + } return getDestinationTimeTableRow(train)?.station; } @@ -39,15 +74,18 @@ export function getTrainDepartureStationName(train: TrainByStationFragment) { return departureStation ? getTrainStationName(departureStation) : undefined; } -export function getTrainDestinationStationName(train: TrainByStationFragment) { - const destinationStation = getTrainDestinationStation(train); +export function getTrainDestinationStationName( + train: TrainByStationFragment, + stationCode?: string +) { + const destinationStation = getTrainDestinationStation(train, stationCode); return destinationStation ? getTrainStationName(destinationStation) : undefined; } export function getTrainStationName(station: { name: string }) { - return station.name.replace('asema', '').trimEnd(); + return station.name.replace(' asema', '').trimEnd(); } export function getWagonNumberFromVehicleId(