mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
e6e520b78f
A hotel row now takes its position and name from the trip's place instead of the stay row, which the planner fetches again only when a stay is edited. A moved pin no longer seats the hotel at its old spot while the server plans from the new one. The drive out of the morning hotel goes the way the first place is reached from it, the mode Days draws that leg in, and it is set in the shared rule so the browser and calculate_roadtrip agree. On the rail, a hotel row that took in the morning marker starts the line. A day that only drives from one stay to the next shows no stop count and takes a dropped stop as its first. The hotel keeps its pin on the desktop map, and the switch explains itself under its label and to screen readers. On the phone, the note about figures set on the desktop now sits above the switch it does not cover. The hint and the refusal on the hotel's drive say what they mean in all 23 languages. The via tools and the wiki say that a via into a booked night is kept but not used, the wiki states the overlap rule Days applies, and the arrived-from branch of the via anchor has tests.
79 lines
4.0 KiB
TypeScript
79 lines
4.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { BedDouble } from 'lucide-react'
|
|
import { BOOKEND_ICON, bookendBooking, bookendMeta, bookendTitle, staysAtTheirPlaces } from './nightBookend'
|
|
import type { BookendReading } from './roadtripRowModel'
|
|
|
|
// FE-BOOKEND-001 to FE-BOOKEND-006
|
|
|
|
const reading = (over: Partial<BookendReading> = {}): BookendReading => ({
|
|
phase: 'morning',
|
|
variant: 'from',
|
|
name: 'Hotel Alpenblick',
|
|
until: null,
|
|
reservationId: 41,
|
|
accommodationId: 5,
|
|
placeId: 900,
|
|
...over,
|
|
})
|
|
|
|
/** A translator that shows the key and what went into it, so a test reads which sentence was asked for. */
|
|
const t = (key: string, params?: Record<string, string | number | null>) =>
|
|
params ? `${key}(${Object.entries(params).map(([k, v]) => `${k}=${v}`).join(',')})` : key
|
|
|
|
describe('a booked night as the rail and the phone say it', () => {
|
|
it('FE-BOOKEND-001: each of the four readings is its own sentence, with the stay named in it', () => {
|
|
expect(bookendTitle(reading({ variant: 'checkOut' }), t)).toBe('roadtrip.bookend.checkOut(name=Hotel Alpenblick)')
|
|
expect(bookendTitle(reading({ variant: 'from' }), t)).toBe('roadtrip.bookend.from(name=Hotel Alpenblick)')
|
|
expect(bookendTitle(reading({ phase: 'evening', variant: 'back' }), t)).toBe('roadtrip.bookend.back(name=Hotel Alpenblick)')
|
|
expect(bookendTitle(reading({ phase: 'evening', variant: 'checkIn' }), t)).toBe('roadtrip.bookend.checkIn(name=Hotel Alpenblick)')
|
|
})
|
|
|
|
it('FE-BOOKEND-002: the check-out hour is a label in the reader\'s clock', () => {
|
|
expect(bookendMeta(reading({ variant: 'checkOut', until: '10:00' }), t, false)).toBe('roadtrip.stay.until(time=10:00)')
|
|
expect(bookendMeta(reading({ variant: 'checkOut', until: '14:30' }), t, true)).toBe('roadtrip.stay.until(time=2:30 PM)')
|
|
})
|
|
|
|
it('FE-BOOKEND-003: nothing under the line when the morning hands no room back', () => {
|
|
expect(bookendMeta(reading(), t, false)).toBeNull()
|
|
expect(bookendMeta(reading({ phase: 'evening', variant: 'back' }), t, false)).toBeNull()
|
|
})
|
|
|
|
it('FE-BOOKEND-004: a tap opens the booking behind the night for somebody who may edit bookings', () => {
|
|
expect(bookendBooking(reading(), true)).toBe(41)
|
|
// The editor is the only view a hotel booking has, so anybody else gets the place.
|
|
expect(bookendBooking(reading(), false)).toBeNull()
|
|
// A stay entered without a booking has none to open.
|
|
expect(bookendBooking(reading({ reservationId: null }), true)).toBeNull()
|
|
})
|
|
|
|
it('FE-BOOKEND-005: the hotel wears the bed the stay chips wear', () => {
|
|
expect(BOOKEND_ICON).toBe(BedDouble)
|
|
})
|
|
})
|
|
|
|
describe('the hotel a booked night stands for', () => {
|
|
it('FE-BOOKEND-006: is read off the trip place as it is now, and the stay row stands in only where there is none', () => {
|
|
const moved = { id: 1, place_id: 10, place_name: 'Old name', place_lat: 47.2, place_lng: 11.4 }
|
|
const unknown = { id: 2, place_id: 20, place_name: 'Elsewhere', place_lat: 48.1, place_lng: 11.6 }
|
|
const placeless = { id: 3, place_id: null, place_name: null, place_lat: null, place_lng: null }
|
|
const places = [
|
|
{ id: 10, name: 'Hotel Alpenblick', lat: 47.3, lng: 11.5 },
|
|
{ id: 30, name: 'Unrelated', lat: 1, lng: 2 },
|
|
]
|
|
|
|
const read = staysAtTheirPlaces([moved, unknown, placeless], places)
|
|
expect(read[0]).toEqual({ id: 1, place_id: 10, place_name: 'Hotel Alpenblick', place_lat: 47.3, place_lng: 11.5 })
|
|
expect(read[1]).toBe(unknown)
|
|
expect(read[2]).toBe(placeless)
|
|
|
|
// A place that lost its pin takes the bookend with it, as the server's join does.
|
|
expect(staysAtTheirPlaces([moved], [{ id: 10, name: 'Hotel Alpenblick', lat: null, lng: null }])[0])
|
|
.toMatchObject({ place_lat: null, place_lng: null })
|
|
|
|
// Nothing to change hands the list back as it came, so nothing downstream recomputes.
|
|
const current = [{ ...moved, place_name: 'Hotel Alpenblick', place_lat: 47.3, place_lng: 11.5 }]
|
|
expect(staysAtTheirPlaces(current, places)).toBe(current)
|
|
expect(staysAtTheirPlaces(current, [])).toBe(current)
|
|
})
|
|
})
|