Files
TREK/client/tests/unit/mobile/trip/MTransportSheet.test.tsx
T
Maurice a0554c2346 fix: four reported bugs that dev still had
- Dashboard: a browser that names no time zone (Chrome on Windows for a zone
  it cannot map) crashed the world clocks and the whole dashboard. One helper
  now reads the browser zone and every reader picks its fallback; a stored
  zone the browser does not know is left out instead of throwing (#2694).
- Phone: a leg of a multi-leg train or flight showed only its route, and its
  sheet showed the first leg's seat. The day plan subtitle is one helper for
  desktop and phone now, so a leg names its own train or flight, platform and
  seat, translated instead of the hardcoded "Gl."/"Sitz"; the sheet opened
  from a leg shows that leg's number, seat, platform and times (#2701). The
  PDF export translates platform and seat too.
- Journey list: the year badge named the year a journey was written, not the
  years of its trips; a trip over New Year shows both (#2685).
- A place's description and notes are markdown in the planner but printed as
  raw syntax in the PDF export and on a shared trip. Both render it now through
  the app's sanitized markdown pipeline, raw HTML stays text (#2692).

Closes #2694
Closes #2701
Closes #2685
Closes #2692
2026-10-24 10:39:35 +02:00

476 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import MTransportSheet from '../../../../src/mobile/screens/trip/sheets/MTransportSheet'
import { openFile } from '../../../../src/utils/fileDownload'
import { useSettingsStore } from '../../../../src/store/settingsStore'
import type { Reservation, TripFile } from '../../../../src/types'
import { buildPlanner, buildShell } from '../../../helpers/mobileTrip'
import { resetAllStores, seedStore } from '../../../helpers/store'
import { fireEvent, render, screen } from '../../../helpers/render'
// FE-MOB-TRSH-001 to FE-MOB-TRSH-035
vi.mock('../../../../src/utils/fileDownload', () => ({ openFile: vi.fn() }))
const FLIGHT = {
id: 7,
trip_id: 1,
type: 'flight',
title: 'FRA → HND',
status: 'pending',
day_id: 3,
reservation_time: '2026-05-01T10:20',
reservation_end_time: '2026-05-02T06:45',
confirmation_number: 'QW7788',
notes: 'Window seat requested',
metadata: JSON.stringify({ airline: 'Lufthansa', flight_number: 'LH 716', seat: '31A' }),
endpoints: [
{ id: 1, reservation_id: 7, role: 'from', sequence: 0, name: 'Frankfurt (FRA)', code: 'FRA', lat: 50.03, lng: 8.57, timezone: 'Europe/Berlin', local_date: '2026-05-01', local_time: '10:20' },
{ id: 2, reservation_id: 7, role: 'to', sequence: 1, name: 'Tokyo (HND)', code: 'HND', lat: 35.55, lng: 139.78, timezone: 'Asia/Tokyo', local_date: '2026-05-02', local_time: '06:45' },
],
} as unknown as Reservation
function makePlanner(overrides: Record<string, unknown> = {}) {
return buildPlanner({
reservations: [FLIGHT],
files: [],
...overrides,
} as unknown as Parameters<typeof buildPlanner>[0])
}
function makeShell(overrides: Record<string, unknown> = {}) {
return buildShell({ sheet: { id: 'transport', payload: { reservationId: 7 } }, ...overrides } as unknown as Parameters<typeof buildShell>[0])
}
describe('MTransportSheet', () => {
beforeEach(() => {
resetAllStores()
})
afterEach(() => {
vi.clearAllMocks()
})
it('FE-MOB-TRSH-001: renders nothing when the payload points at an unknown reservation', () => {
render(<MTransportSheet planner={makePlanner({ reservations: [] })} shell={makeShell()} />)
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})
it('FE-MOB-TRSH-002: stays closed while another sheet id is active', () => {
render(<MTransportSheet planner={makePlanner()} shell={makeShell({ sheet: { id: 'mehr', payload: { reservationId: 7 } } })} />)
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})
it('FE-MOB-TRSH-003: shows title, airline meta and the endpoint route in the subline', () => {
render(<MTransportSheet planner={makePlanner()} shell={makeShell()} />)
expect(screen.getByRole('dialog', { name: 'FRA → HND' })).toBeInTheDocument()
expect(screen.getByText('Lufthansa · LH 716 · Frankfurt (FRA) → Tokyo (HND)')).toBeInTheDocument()
})
it('FE-MOB-TRSH-004: falls back to the formatted date when the booking has no endpoints', () => {
const res = { ...FLIGHT, endpoints: [], metadata: null } as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByText('Fri, May 1')).toBeInTheDocument()
})
it('FE-MOB-TRSH-005: shows departure and arrival stat boxes labelled with the endpoint names', () => {
render(<MTransportSheet planner={makePlanner()} shell={makeShell()} />)
expect(screen.getByText('10:20')).toBeInTheDocument()
expect(screen.getByText('06:45')).toBeInTheDocument()
expect(screen.getByText('Frankfurt (FRA)')).toBeInTheDocument()
expect(screen.getByText('Tokyo (HND)')).toBeInTheDocument()
expect(screen.getByText('31A')).toBeInTheDocument()
expect(screen.getByText('Seat')).toBeInTheDocument()
})
it('FE-MOB-TRSH-006: renders the ISO reservation time in the 12h format and the generic time label', () => {
seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, time_format: '12h' } })
const res = {
...FLIGHT,
endpoints: [],
reservation_time: '2026-05-01T15:30:00Z',
reservation_end_time: null,
metadata: null,
} as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByText(/PM$/)).toBeInTheDocument()
expect(screen.getByText('Time')).toBeInTheDocument()
})
it('FE-MOB-TRSH-007: shows the train number and platform of a train booking', () => {
const res = {
...FLIGHT, type: 'train', title: 'ICE to Berlin',
metadata: { train_number: 'ICE 599', platform: '7', seat: '12C' },
} as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByText(/ICE 599/)).toBeInTheDocument()
expect(screen.getByText('7')).toBeInTheDocument()
expect(screen.getByText('Platform')).toBeInTheDocument()
})
it('FE-MOB-TRSH-008: survives unparsable metadata without meta rows', () => {
const res = { ...FLIGHT, metadata: '{not json', endpoints: [] } as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByRole('dialog', { name: 'FRA → HND' })).toBeInTheDocument()
expect(screen.queryByText('Seat')).not.toBeInTheDocument()
})
it('FE-MOB-TRSH-009: renders the transit itinerary with line, walk, duration and stop counts', () => {
const res = {
...FLIGHT, type: 'transit', title: 'To the museum', endpoints: [], metadata: {
transit: {
legs: [
{ mode: 'WALK', duration: 240, to: { name: 'Karlsplatz' } },
{
mode: 'SUBWAY', line: 'U4', line_color: '#00a94f', line_text_color: '#fff', duration: 600, stops: 4,
from: { name: 'Karlsplatz', time: '09:10' }, to: { name: 'Schwedenplatz', time: '09:20' },
},
{ mode: 'BUS', line: null, duration: 0, from: { name: 'Schwedenplatz', time: null }, to: { name: 'Prater' } },
],
},
},
} as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByText('Walk to Karlsplatz')).toBeInTheDocument()
expect(screen.getByText('4 min')).toBeInTheDocument()
expect(screen.getByText('U4')).toBeInTheDocument()
expect(screen.getByText('09:10 – 09:20 · 10 min · 4 stops')).toBeInTheDocument()
// A leg without a line falls back to its mode and renders no meta line.
expect(screen.getByText('BUS')).toBeInTheDocument()
expect(screen.getByText('Prater')).toBeInTheDocument()
})
it('FE-MOB-TRSH-010: shows the pending status and the booking code unblurred by default', () => {
render(<MTransportSheet planner={makePlanner()} shell={makeShell()} />)
expect(screen.getByText('Pending')).toBeInTheDocument()
const code = screen.getByText('#QW7788')
expect(code.className).not.toContain('blur')
})
it('FE-MOB-TRSH-011: blurs the booking code until it is tapped', () => {
seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, blur_booking_codes: true } })
const res = { ...FLIGHT, status: 'confirmed' } as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getByText('Confirmed')).toBeInTheDocument()
const code = screen.getByText('#QW7788')
expect(code.className).toContain('blur-[4px]')
fireEvent.click(code)
expect(screen.getByText('#QW7788').className).not.toContain('blur-[4px]')
})
it('FE-MOB-TRSH-012: re-blurs the code once the sheet is closed again', () => {
seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, blur_booking_codes: true } })
const planner = makePlanner()
const { rerender } = render(<MTransportSheet planner={planner} shell={makeShell()} />)
fireEvent.click(screen.getByText('#QW7788'))
expect(screen.getByText('#QW7788').className).not.toContain('blur-[4px]')
rerender(<MTransportSheet planner={planner} shell={makeShell({ sheet: null })} />)
rerender(<MTransportSheet planner={planner} shell={makeShell()} />)
expect(screen.getByText('#QW7788').className).toContain('blur-[4px]')
})
it('FE-MOB-TRSH-013: renders the notes block only when the booking has notes', () => {
render(<MTransportSheet planner={makePlanner()} shell={makeShell()} />)
expect(screen.getByText('Window seat requested')).toBeInTheDocument()
const res = { ...FLIGHT, notes: null } as unknown as Reservation
render(<MTransportSheet planner={makePlanner({ reservations: [res] })} shell={makeShell()} />)
expect(screen.getAllByText('Notes')).toHaveLength(1)
})
it('FE-MOB-TRSH-014: drawing the route closes the sheet and jumps to the plan map', () => {
const planner = makePlanner()
const shell = makeShell({ trTab: 'transports', view: 'plan' })
render(<MTransportSheet planner={planner} shell={shell} />)
const btn = screen.getByRole('button', { name: 'On map' })
expect(btn).toHaveAttribute('aria-pressed', 'false')
fireEvent.click(btn)
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(shell.closeSheet).toHaveBeenCalledTimes(1)
expect(shell.setTrTab).toHaveBeenCalledWith('plan')
expect(shell.toggleView).toHaveBeenCalledTimes(1)
})
it('FE-MOB-TRSH-015: hides an already drawn route without navigating away', () => {
const planner = makePlanner({ visibleConnections: [7] })
const shell = makeShell({ trTab: 'plan', view: 'map' })
render(<MTransportSheet planner={planner} shell={shell} />)
const btn = screen.getByRole('button', { name: 'On map' })
expect(btn).toHaveAttribute('aria-pressed', 'true')
fireEvent.click(btn)
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(shell.closeSheet).not.toHaveBeenCalled()
expect(shell.setTrTab).not.toHaveBeenCalled()
expect(shell.toggleView).not.toHaveBeenCalled()
})
it('FE-MOB-TRSH-016: lists the attached files and opens one on tap', () => {
const files = [
{ id: 1, reservation_id: 7, original_name: 'boardingpass.pdf', url: '/api/trips/1/files/1/download' },
{ id: 2, reservation_id: null, linked_reservation_ids: [7], original_name: 'invoice.pdf', url: '/api/trips/1/files/2/download' },
{ id: 3, reservation_id: 7, deleted_at: '2026-04-01', original_name: 'trashed.pdf', url: '/x' },
{ id: 4, reservation_id: 99, original_name: 'other.pdf', url: '/y' },
] as unknown as TripFile[]
render(<MTransportSheet planner={makePlanner({ files })} shell={makeShell()} />)
expect(screen.getByText('boardingpass.pdf')).toBeInTheDocument()
expect(screen.getByText('invoice.pdf')).toBeInTheDocument()
expect(screen.queryByText('trashed.pdf')).not.toBeInTheDocument()
expect(screen.queryByText('other.pdf')).not.toBeInTheDocument()
fireEvent.click(screen.getByText('boardingpass.pdf'))
expect(openFile).toHaveBeenCalledWith('/api/trips/1/files/1/download', 'boardingpass.pdf')
})
it('FE-MOB-TRSH-017: opens the transport editor for the booking', () => {
const planner = makePlanner()
const shell = makeShell()
render(<MTransportSheet planner={planner} shell={shell} />)
fireEvent.click(screen.getByRole('button', { name: 'Edit' }))
expect(planner.setEditingTransport).toHaveBeenCalledWith(expect.objectContaining({ id: 7 }))
expect(planner.setTransportModalDayId).toHaveBeenCalledWith(3)
expect(planner.setShowTransportModal).toHaveBeenCalledWith(true)
expect(shell.closeSheet).toHaveBeenCalledTimes(1)
})
it('FE-MOB-TRSH-018: passes a null day id to the editor when the booking has none', () => {
const res = { ...FLIGHT, day_id: null } as unknown as Reservation
const planner = makePlanner({ reservations: [res] })
render(<MTransportSheet planner={planner} shell={makeShell()} />)
fireEvent.click(screen.getByRole('button', { name: 'Edit' }))
expect(planner.setTransportModalDayId).toHaveBeenCalledWith(null)
})
it('FE-MOB-TRSH-019: deletes the booking and closes the sheet', () => {
const planner = makePlanner()
const shell = makeShell()
render(<MTransportSheet planner={planner} shell={shell} />)
fireEvent.click(screen.getByRole('button', { name: 'Delete' }))
expect(planner.handleDeleteReservation).toHaveBeenCalledWith(7)
expect(shell.closeSheet).toHaveBeenCalledTimes(1)
})
it('FE-MOB-TRSH-020: hides edit and delete without the day_edit permission but keeps the map toggle', () => {
const planner = makePlanner({ can: vi.fn(() => false) })
render(<MTransportSheet planner={planner} shell={makeShell()} />)
expect(screen.queryByRole('button', { name: 'Edit' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'Delete' })).not.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'On map' })).toBeInTheDocument()
})
it('FE-MOB-TRSH-021: keeps the last booking on screen while the sheet animates out', () => {
const planner = makePlanner()
const shell = makeShell()
const { rerender } = render(<MTransportSheet planner={planner} shell={shell} />)
expect(screen.getByText('FRA → HND')).toBeInTheDocument()
rerender(<MTransportSheet planner={makePlanner({ reservations: [] })} shell={shell} />)
expect(screen.getByText('FRA → HND')).toBeInTheDocument()
})
const layover = (): Reservation => ({
...FLIGHT,
metadata: JSON.stringify({
airline: 'Lufthansa', flight_number: 'LH 716',
legs: [
{ from: 'FRA', to: 'BER', airline: 'Lufthansa', flight_number: 'LH 716', confirmation_number: 'ABC123' },
{ from: 'BER', to: 'HND', airline: 'ANA', flight_number: 'NH 204' },
],
}),
} as unknown as Reservation)
it('FE-MOB-TRSH-023: lists the booking code of every segment that has its own (#1943)', () => {
render(<MTransportSheet planner={makePlanner({ reservations: [layover()] })} shell={makeShell()} />)
// The booking's own reference keeps its row above.
expect(screen.getByText('#QW7788')).toBeInTheDocument()
expect(screen.getByText('FRA → BER')).toBeInTheDocument()
expect(screen.getByText('#ABC123')).toBeInTheDocument()
// A segment without one contributes no row.
expect(screen.queryByText('BER → HND')).not.toBeInTheDocument()
})
it('FE-MOB-TRSH-024: a segment code is blurred until it is tapped', () => {
seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, blur_booking_codes: true } })
render(<MTransportSheet planner={makePlanner({ reservations: [layover()] })} shell={makeShell()} />)
const code = screen.getByText('#ABC123')
expect(code.className).toContain('blur-[4px]')
fireEvent.click(code)
expect(screen.getByText('#ABC123').className).not.toContain('blur-[4px]')
})
it('FE-MOB-TRSH-022: closes from the header button', () => {
const shell = makeShell()
render(<MTransportSheet planner={makePlanner()} shell={shell} />)
fireEvent.click(screen.getByRole('button', { name: 'Close' }))
expect(shell.closeSheet).toHaveBeenCalledTimes(1)
})
/**
* #2456: the phone map is one day's picture, so "On map" has to land on a day the
* booking runs on. The Transports tab lists the whole trip, and the day the chips hold
* can be any other day: switched on from there, the route went to a map that frames,
* and (once booking routes follow the day) draws, a different day.
*
* The booking's day goes in after the view switch: entering the map re-selects the day
* the shell still holds (MTripShell.toggleView), so it has to come last to win.
*/
describe('On map lands on a day the booking runs on (#2456)', () => {
const DAYS = [
{ id: 3, trip_id: 1, day_number: 1, date: '2026-05-01' },
{ id: 4, trip_id: 1, day_number: 2, date: '2026-05-02' },
{ id: 5, trip_id: 1, day_number: 3, date: '2026-05-03' },
]
it("FE-MOB-TRSH-025: from another day, switches the map to the booking's own day", () => {
const planner = makePlanner({ days: DAYS, selectedDayId: 5 })
const shell = makeShell({ trTab: 'transports', view: 'plan' })
render(<MTransportSheet planner={planner} shell={shell} />)
fireEvent.click(screen.getByRole('button', { name: 'On map' }))
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(planner.handleSelectDay).toHaveBeenCalledWith(3, false)
expect(planner.setExpandedDayIds).toHaveBeenCalledWith(new Set([3]))
const viewSwitch = vi.mocked(shell.toggleView).mock.invocationCallOrder[0]
const daySwitches = vi.mocked(planner.handleSelectDay).mock.invocationCallOrder
expect(daySwitches[daySwitches.length - 1]).toBeGreaterThan(viewSwitch)
})
it("FE-MOB-TRSH-026: with the map already in front, still moves it to the booking's day", () => {
const planner = makePlanner({ days: DAYS, selectedDayId: 5 })
const shell = makeShell({ trTab: 'transports', view: 'map' })
render(<MTransportSheet planner={planner} shell={shell} />)
fireEvent.click(screen.getByRole('button', { name: 'On map' }))
expect(shell.toggleView).not.toHaveBeenCalled()
expect(planner.handleSelectDay).toHaveBeenCalledWith(3, false)
expect(planner.setExpandedDayIds).toHaveBeenCalledWith(new Set([3]))
})
it("FE-MOB-TRSH-027: keeps the selected day while it is one of the booking's days", () => {
// Overnight: departs on day 3, lands on day 4, which the chips hold.
const overnight = { ...FLIGHT, end_day_id: 4 } as unknown as Reservation
const planner = makePlanner({ days: DAYS, selectedDayId: 4, reservations: [overnight] })
render(<MTransportSheet planner={planner} shell={makeShell({ trTab: 'plan', view: 'plan' })} />)
fireEvent.click(screen.getByRole('button', { name: 'On map' }))
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(planner.handleSelectDay).not.toHaveBeenCalled()
expect(planner.setExpandedDayIds).not.toHaveBeenCalled()
})
it('FE-MOB-TRSH-028: leaves the all days view and a booking bound to no day alone', () => {
const unbound = { ...FLIGHT, day_id: null, end_day_id: null } as unknown as Reservation
const allDays = makePlanner({ days: DAYS, selectedDayId: null })
const { unmount } = render(<MTransportSheet planner={allDays} shell={makeShell({ trTab: 'transports', view: 'map' })} />)
fireEvent.click(screen.getByRole('button', { name: 'On map' }))
expect(allDays.handleSelectDay).not.toHaveBeenCalled()
unmount()
const noDay = makePlanner({ days: DAYS, selectedDayId: 5, reservations: [unbound] })
render(<MTransportSheet planner={noDay} shell={makeShell({ trTab: 'transports', view: 'plan' })} />)
fireEvent.click(screen.getByRole('button', { name: 'On map' }))
expect(noDay.toggleConnection).toHaveBeenCalledWith(7)
expect(noDay.handleSelectDay).not.toHaveBeenCalled()
})
// A booking already switched on is still left off another day's map, which is also
// where "Always show booking routes" leaves every booking. The button follows the map
// there, and a tap takes it to the booking rather than switching the route off unseen.
it('FE-MOB-TRSH-029: a booking switched on but not drawn on this day reads off and a tap moves to its day', () => {
const planner = makePlanner({ days: DAYS, selectedDayId: 5, visibleConnections: [7] })
const shell = makeShell({ trTab: 'transports', view: 'plan' })
render(<MTransportSheet planner={planner} shell={shell} />)
const btn = screen.getByRole('button', { name: 'On map' })
expect(btn).toHaveAttribute('aria-pressed', 'false')
fireEvent.click(btn)
expect(planner.toggleConnection).not.toHaveBeenCalled()
expect(shell.closeSheet).toHaveBeenCalledTimes(1)
expect(shell.setTrTab).toHaveBeenCalledWith('plan')
expect(planner.handleSelectDay).toHaveBeenCalledWith(3, false)
expect(planner.setExpandedDayIds).toHaveBeenCalledWith(new Set([3]))
})
it("FE-MOB-TRSH-030: on one of the booking's days a switched-on route reads on and a tap hides it in place", () => {
const overnight = { ...FLIGHT, end_day_id: 4 } as unknown as Reservation
const planner = makePlanner({ days: DAYS, selectedDayId: 4, reservations: [overnight], visibleConnections: [7] })
const shell = makeShell({ trTab: 'plan', view: 'map' })
render(<MTransportSheet planner={planner} shell={shell} />)
const btn = screen.getByRole('button', { name: 'On map' })
expect(btn).toHaveAttribute('aria-pressed', 'true')
fireEvent.click(btn)
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(shell.closeSheet).not.toHaveBeenCalled()
expect(planner.handleSelectDay).not.toHaveBeenCalled()
})
})
describe('a booking the chat points at', () => {
it('FE-MOB-TRSH-031: ?booking= of a transport opens its sheet once and leaves the URL', () => {
const shell = makeShell({ sheet: null })
render(<MTransportSheet planner={makePlanner()} shell={shell} />, { initialEntries: ['/trips/1?booking=7'] })
expect(shell.openSheet).toHaveBeenCalledTimes(1)
expect(shell.openSheet).toHaveBeenCalledWith('transport', { reservationId: 7 })
})
it('FE-MOB-TRSH-032: a booking that is no transport, or an id the trip does not hold, opens nothing', () => {
const dinner = { ...FLIGHT, id: 8, type: 'restaurant', endpoints: [] } as unknown as Reservation
const shell = makeShell({ sheet: null })
render(<MTransportSheet planner={makePlanner({ reservations: [FLIGHT, dinner] })} shell={shell} />, {
initialEntries: ['/trips/1?booking=8'],
})
render(<MTransportSheet planner={makePlanner()} shell={shell} />, { initialEntries: ['/trips/1?booking=99'] })
expect(shell.openSheet).not.toHaveBeenCalled()
})
it('FE-MOB-TRSH-033: with &map=1 the route is switched on and the map shown on its day, no sheet opened', () => {
const days = [
{ id: 3, trip_id: 1, day_number: 1, date: '2026-05-01' },
{ id: 5, trip_id: 1, day_number: 3, date: '2026-05-03' },
]
const planner = makePlanner({ days, selectedDayId: 5 })
const shell = makeShell({ sheet: null, trTab: 'transports', view: 'plan' })
render(<MTransportSheet planner={planner} shell={shell} />, { initialEntries: ['/trips/1?booking=7&map=1'] })
expect(shell.openSheet).not.toHaveBeenCalled()
expect(planner.toggleConnection).toHaveBeenCalledTimes(1)
expect(planner.toggleConnection).toHaveBeenCalledWith(7)
expect(shell.setTrTab).toHaveBeenCalledWith('plan')
expect(shell.toggleView).toHaveBeenCalledTimes(1)
expect(planner.handleSelectDay).toHaveBeenCalledWith(3, false)
})
})
// #2701: a leg row opens the sheet on that leg, not on the booking's first one.
describe('opened from one leg of a multi-leg train', () => {
const TRAIN = {
...FLIGHT, id: 8, type: 'train', title: 'Takayama to Kyoto', confirmation_number: null,
metadata: JSON.stringify({
train_number: 'Hida 6', seat: '10-4C',
legs: [
{ from: 'Takayama', to: 'Nagoya', train_number: 'Hida 6', seat: '10-4C', platform: '1', dep_time: '09:40', arr_time: '12:10' },
{ from: 'Nagoya', to: 'Kyoto', train_number: 'Nozomi 67', seat: '4-5A', platform: '14', dep_time: '12:35', arr_time: '13:10' },
],
}),
} as unknown as Reservation
it("FE-MOB-TRSH-034: shows the second leg's own train, seat, platform and times", () => {
render(<MTransportSheet planner={makePlanner({ reservations: [TRAIN] })} shell={makeShell({ sheet: { id: 'transport', payload: { reservationId: 8, legIndex: 1 } } })} />)
expect(screen.getByText(/Nozomi 67/)).toBeInTheDocument()
expect(screen.getByText('4-5A')).toBeInTheDocument()
expect(screen.getByText('14')).toBeInTheDocument()
expect(screen.getByText('12:35')).toBeInTheDocument()
expect(screen.getByText('13:10')).toBeInTheDocument()
expect(screen.queryByText('10-4C')).not.toBeInTheDocument()
})
it('FE-MOB-TRSH-035: without a leg the sheet still shows the booking as a whole', () => {
render(<MTransportSheet planner={makePlanner({ reservations: [TRAIN] })} shell={makeShell({ sheet: { id: 'transport', payload: { reservationId: 8 } } })} />)
expect(screen.getByText('10-4C')).toBeInTheDocument()
expect(screen.queryByText('4-5A')).not.toBeInTheDocument()
})
})
})