mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
93dddaa4af
A booking switched on with "On map" stayed drawn on every day of the phone's plan map, so a flight from the first day crossed the map of the third, although a day chip frames one day and drops the other days' pins. The plan map now draws such a booking only on the days it runs on, the arrival day of an overnight one included, and the all-days view still shows every booking that is switched on. The switch itself stays trip-wide, and the desktop, collections and the road trip stage draw exactly as before. "On map" in a booking's sheet now reads as on only where the plan map draws the route. From any other day a tap takes the map to the booking's day, switching the route on when it is off, instead of hiding a route that is not drawn there anyway. The phone settings also gain the "Always show booking routes" switch the desktop already had. As on the desktop it sets the default for trips not yet touched on this device, so a trip where a route was switched on or off by hand keeps that choice.
243 lines
10 KiB
TypeScript
243 lines
10 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { hopIsVisible, isRoutableReservation, labelFloorPx, lineFloorPx, runsOnDay, visibleRouteReservations } from './reservationRoutes'
|
|
import type { Day, Reservation, ReservationEndpoint } from '../types'
|
|
|
|
function endpoint(role: 'from' | 'to', lat: number, lng: number): ReservationEndpoint {
|
|
return { role, sequence: role === 'from' ? 0 : 1, name: role, code: null, lat, lng, timezone: null, local_time: null, local_date: null }
|
|
}
|
|
|
|
function reservation(overrides: Partial<Reservation> = {}): Reservation {
|
|
return {
|
|
id: 1, trip_id: 1, title: 'Flight', type: 'flight', status: 'confirmed',
|
|
reservation_time: null, reservation_end_time: null, location: null,
|
|
confirmation_number: null, notes: null, url: null,
|
|
...overrides,
|
|
} as Reservation
|
|
}
|
|
|
|
describe('isRoutableReservation', () => {
|
|
it('is false with no endpoints', () => {
|
|
expect(isRoutableReservation(reservation())).toBe(false)
|
|
})
|
|
|
|
it('is false with a single endpoint', () => {
|
|
expect(isRoutableReservation(reservation({ endpoints: [endpoint('from', 1, 2)] }))).toBe(false)
|
|
})
|
|
|
|
it('is true with 2+ endpoints', () => {
|
|
expect(isRoutableReservation(reservation({ endpoints: [endpoint('from', 1, 2), endpoint('to', 3, 4)] }))).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('visibleRouteReservations', () => {
|
|
const twoStop = [endpoint('from', 1, 2), endpoint('to', 3, 4)]
|
|
|
|
it('includes a transit reservation only when showTransitRoutes is on', () => {
|
|
const r = reservation({ id: 1, type: 'transit', endpoints: twoStop })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: false })).toEqual([])
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true })).toEqual([r])
|
|
})
|
|
|
|
it('includes a reservation whose id is in visibleConnectionIds regardless of type', () => {
|
|
const r = reservation({ id: 5, type: 'flight', endpoints: twoStop })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [5], showTransitRoutes: false })).toEqual([r])
|
|
})
|
|
|
|
it('excludes a routable reservation when neither rule applies', () => {
|
|
const r = reservation({ id: 7, type: 'flight', endpoints: twoStop })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: false })).toEqual([])
|
|
})
|
|
|
|
it('does not duplicate a reservation matched by both rules', () => {
|
|
const r = reservation({ id: 10, type: 'transit', endpoints: twoStop })
|
|
const result = visibleRouteReservations([r], { visibleConnectionIds: [10], showTransitRoutes: true })
|
|
expect(result).toEqual([r])
|
|
})
|
|
})
|
|
|
|
describe('visibleRouteReservations with a selected day', () => {
|
|
const twoStop = [endpoint('from', 1, 2), endpoint('to', 3, 4)]
|
|
const days = [
|
|
{ id: 10, trip_id: 1, day_number: 1 },
|
|
{ id: 11, trip_id: 1, day_number: 2 },
|
|
{ id: 20, trip_id: 1, day_number: 3 },
|
|
] as Day[]
|
|
|
|
function transit(overrides: Partial<Reservation>): Reservation {
|
|
return reservation({ type: 'transit', endpoints: twoStop, ...overrides })
|
|
}
|
|
|
|
it('excludes a transit journey that runs on another day', () => {
|
|
const r = transit({ id: 1, day_id: 20, end_day_id: 20 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 10, days })).toEqual([])
|
|
})
|
|
|
|
it('includes a transit journey that runs on the selected day', () => {
|
|
const r = transit({ id: 1, day_id: 10, end_day_id: 10 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 10, days })).toEqual([r])
|
|
})
|
|
|
|
it('keeps an overnight journey on both its departure and its arrival day', () => {
|
|
const r = transit({ id: 1, day_id: 10, end_day_id: 11 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 10, days })).toEqual([r])
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 11, days })).toEqual([r])
|
|
})
|
|
|
|
it('keeps a multi-day journey on the days in between', () => {
|
|
const r = transit({ id: 1, day_id: 10, end_day_id: 20 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 11, days })).toEqual([r])
|
|
})
|
|
|
|
it('goes by day order, not by day id', () => {
|
|
// Days dragged into a new order: id 20 is now the first day, id 10 the last.
|
|
const reordered = [
|
|
{ id: 20, trip_id: 1, day_number: 1 },
|
|
{ id: 11, trip_id: 1, day_number: 2 },
|
|
{ id: 10, trip_id: 1, day_number: 3 },
|
|
] as Day[]
|
|
const r = transit({ id: 1, day_id: 20, end_day_id: 11 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 11, days: reordered })).toEqual([r])
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 10, days: reordered })).toEqual([])
|
|
})
|
|
|
|
it('keeps a transit journey that is bound to no day', () => {
|
|
const r = transit({ id: 1, day_id: null, end_day_id: null })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: 10, days })).toEqual([r])
|
|
})
|
|
|
|
it('leaves the per-item route toggle day-agnostic', () => {
|
|
const r = reservation({ id: 5, type: 'train', endpoints: twoStop, day_id: 20, end_day_id: 20 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [5], showTransitRoutes: false, selectedDayId: 10, days })).toEqual([r])
|
|
})
|
|
|
|
it('does not scope transit without a selected day', () => {
|
|
const r = transit({ id: 1, day_id: 20, end_day_id: 20 })
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, days })).toEqual([r])
|
|
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true, selectedDayId: null, days })).toEqual([r])
|
|
})
|
|
})
|
|
|
|
// The phone's plan map shows one day, so it asks for the per-item toggle to follow the
|
|
// selected day the way the transit toggle does. The span rule is the transit one.
|
|
describe('visibleRouteReservations with the per-item toggle scoped to the day', () => {
|
|
const twoStop = [endpoint('from', 1, 2), endpoint('to', 3, 4)]
|
|
const days = [
|
|
{ id: 10, trip_id: 1, day_number: 1 },
|
|
{ id: 11, trip_id: 1, day_number: 2 },
|
|
{ id: 20, trip_id: 1, day_number: 3 },
|
|
] as Day[]
|
|
|
|
function booking(overrides: Partial<Reservation>): Reservation {
|
|
return reservation({ id: 5, type: 'flight', endpoints: twoStop, ...overrides })
|
|
}
|
|
|
|
const drawn = (r: Reservation, selectedDayId: number | null | undefined, onDays: Day[] = days) =>
|
|
visibleRouteReservations([r], {
|
|
visibleConnectionIds: [r.id], showTransitRoutes: false, selectedDayId, days: onDays, scopeConnectionsToDay: true,
|
|
})
|
|
|
|
it('leaves out a booking switched on for another day', () => {
|
|
expect(drawn(booking({ day_id: 20, end_day_id: 20 }), 10)).toEqual([])
|
|
})
|
|
|
|
it('draws a booking on the day it runs on', () => {
|
|
const r = booking({ day_id: 10, end_day_id: 10 })
|
|
expect(drawn(r, 10)).toEqual([r])
|
|
})
|
|
|
|
it('keeps an overnight booking on both of its days and off the rest', () => {
|
|
const r = booking({ day_id: 10, end_day_id: 11 })
|
|
expect(drawn(r, 10)).toEqual([r])
|
|
expect(drawn(r, 11)).toEqual([r])
|
|
expect(drawn(r, 20)).toEqual([])
|
|
})
|
|
|
|
it('keeps a multi-day booking on the days in between', () => {
|
|
const r = booking({ day_id: 10, end_day_id: 20 })
|
|
expect(drawn(r, 11)).toEqual([r])
|
|
})
|
|
|
|
it('goes by day order, not by day id', () => {
|
|
const reordered = [
|
|
{ id: 20, trip_id: 1, day_number: 1 },
|
|
{ id: 11, trip_id: 1, day_number: 2 },
|
|
{ id: 10, trip_id: 1, day_number: 3 },
|
|
] as Day[]
|
|
const r = booking({ day_id: 20, end_day_id: 11 })
|
|
expect(drawn(r, 11, reordered)).toEqual([r])
|
|
expect(drawn(r, 10, reordered)).toEqual([])
|
|
})
|
|
|
|
it('keeps a booking that is bound to no day', () => {
|
|
const r = booking({ day_id: null, end_day_id: null })
|
|
expect(drawn(r, 10)).toEqual([r])
|
|
})
|
|
|
|
it('keeps every switched-on booking while no day is selected', () => {
|
|
const r = booking({ day_id: 20, end_day_id: 20 })
|
|
expect(drawn(r, undefined)).toEqual([r])
|
|
expect(drawn(r, null)).toEqual([r])
|
|
})
|
|
|
|
it('still draws nothing that was not switched on', () => {
|
|
const r = booking({ day_id: 10, end_day_id: 10 })
|
|
expect(visibleRouteReservations([r], {
|
|
visibleConnectionIds: [], showTransitRoutes: false, selectedDayId: 10, days, scopeConnectionsToDay: true,
|
|
})).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('runsOnDay', () => {
|
|
const days = [
|
|
{ id: 10, trip_id: 1, day_number: 1 },
|
|
{ id: 11, trip_id: 1, day_number: 2 },
|
|
] as Day[]
|
|
|
|
it('reads a booking with only an arrival day as running on that day', () => {
|
|
expect(runsOnDay({ day_id: null, end_day_id: 11 }, 11, days)).toBe(true)
|
|
expect(runsOnDay({ day_id: null, end_day_id: 11 }, 10, days)).toBe(false)
|
|
})
|
|
|
|
it('does not rule a booking out for a selected day it cannot find', () => {
|
|
expect(runsOnDay({ day_id: 10, end_day_id: 10 }, 99, days)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('hopIsVisible (#2275)', () => {
|
|
// A flat projection: one unit of lat or lng is one pixel.
|
|
const px = (p: readonly [number, number]) => ({ x: p[1], y: p[0] })
|
|
|
|
it('keeps the type floors of the old endpoint check', () => {
|
|
expect(lineFloorPx('car')).toBe(80)
|
|
expect(lineFloorPx('flight')).toBe(50)
|
|
expect(lineFloorPx('cruise')).toBe(150)
|
|
expect(lineFloorPx('train')).toBe(200)
|
|
expect(labelFloorPx('car')).toBe(150)
|
|
expect(labelFloorPx('transit')).toBe(900)
|
|
expect(labelFloorPx('bus')).toBe(400)
|
|
})
|
|
|
|
it('measures a straight two-point line exactly as the endpoint gap', () => {
|
|
expect(hopIsVisible('car', [[[0, 0], [0, 79]]], px)).toBe(false)
|
|
expect(hopIsVisible('car', [[[0, 0], [0, 80]]], px)).toBe(true)
|
|
})
|
|
|
|
it('walks a routed line, so a loop between close ends counts its full length', () => {
|
|
const loop: [number, number][] = [[0, 0], [30, 0], [30, 30], [0, 30], [0, 5]]
|
|
expect(hopIsVisible('car', [loop], px)).toBe(true)
|
|
expect(hopIsVisible('train', [loop], px)).toBe(false)
|
|
})
|
|
|
|
it('adds the legs of a multi-stop booking together', () => {
|
|
const legs: [number, number][][] = [[[0, 0], [0, 50]], [[0, 50], [0, 100]]]
|
|
expect(hopIsVisible('car', legs, px)).toBe(false)
|
|
expect(hopIsVisible('car', [[[0, 0], [0, 50], [0, 100]]], px)).toBe(true)
|
|
})
|
|
|
|
it('draws nothing for an empty or one-point line', () => {
|
|
expect(hopIsVisible('car', [], px)).toBe(false)
|
|
expect(hopIsVisible('car', [[]], px)).toBe(false)
|
|
expect(hopIsVisible('car', [[[0, 0]]], px)).toBe(false)
|
|
})
|
|
})
|