From 00fca4952f8d8dfa638f08317d7c1f6b61711ace Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 12 Oct 2026 21:14:28 +0000 Subject: [PATCH] test(roadtrip): the road trip suite writes stays through the real repo and seeds the searched day once --- .../useTripPlanner.roadtrip.test.tsx | 93 ++++++++----------- 1 file changed, 38 insertions(+), 55 deletions(-) diff --git a/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx b/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx index dac548894..b41c09009 100644 --- a/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx +++ b/client/src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx @@ -63,9 +63,12 @@ vi.mock('../../hooks/useAirtrailConnection', () => ({ useAirtrailConnection: () => ({ airtrailEnabled: false, connected: false, available: false, loading: false }), })) -vi.mock('../../repo/accommodationRepo', () => ({ - accommodationRepo: { list: vi.fn(async () => ({ accommodations: [] })) }, -})) +// Only the list read is stubbed; a stay is written through the real repo, which sends +// it to accommodationsApi like the planner does online. +vi.mock('../../repo/accommodationRepo', async importOriginal => { + const real = await importOriginal() + return { accommodationRepo: { ...real.accommodationRepo, list: vi.fn(async () => ({ accommodations: [] })) } } +}) vi.mock('../../services/photoService', () => ({ getCached: vi.fn(() => undefined), @@ -252,6 +255,12 @@ function seedTrip(extra: Partial = {}) { return trip } +/** A trip of one day, day 5, which the corridor is searching: where most cases start. */ +function seedSearchedDay() { + seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) + rt.corridor.day = { dayId: 5, dayNumber: 1 } +} + /** * Demote the current session to somebody who may look but not touch. * @@ -414,8 +423,7 @@ describe('visit day-end controls', () => { describe('useTripPlanner road trip: a hit on the drive', () => { it('FE-TP-ROAD-001: a corridor hit opens the popup on the day being searched, at its place in the chain', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.insertIndexFor.mockReturnValue(2) const { result } = await renderRoadtrip() @@ -447,8 +455,7 @@ describe('useTripPlanner road trip: a hit on the drive', () => { }) it('FE-TP-ROAD-003: on the last day of the trip the night ends where it began', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() act(() => { result.current.handlePoiClick(poi({ category: 'campsite' }) as never) }) @@ -457,8 +464,7 @@ describe('useTripPlanner road trip: a hit on the drive', () => { }) it('FE-TP-ROAD-004: a fuel stop gets no overnight mode, so the popup stays a dwell dialog', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() act(() => { result.current.handlePoiClick(poi() as never) }) @@ -469,8 +475,7 @@ describe('useTripPlanner road trip: a hit on the drive', () => { it('FE-TP-ROAD-005: an ordinary map POI still goes to the full form, carrying the day being driven', async () => { // Without the day it lands in the unplanned pool, and road trip mode shows no pool, // so a just-added place would disappear without a trace. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() const { alongKm: _drop, offRouteKm: _drop2, ...plain } = poi() @@ -483,8 +488,7 @@ describe('useTripPlanner road trip: a hit on the drive', () => { it('FE-TP-ROAD-006: a reader who may not add places gets nothing at all', async () => { asReader('place_edit') - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() act(() => { result.current.handlePoiClick(poi() as never) }) @@ -496,8 +500,7 @@ describe('useTripPlanner road trip: a hit on the drive', () => { describe('useTripPlanner road trip: saving a hit', () => { const openDraft = async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.insertIndexFor.mockReturnValue(1) const rendered = await renderRoadtrip() act(() => { rendered.result.current.handlePoiClick(poi() as never) }) @@ -533,8 +536,7 @@ describe('useTripPlanner road trip: saving a hit', () => { }) it('FE-TP-ROAD-009: saving with nothing open writes nothing', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() await act(async () => { await result.current.saveStopDraft({ stopType: 'fuel', dwellMinutes: 10 }) }) @@ -623,8 +625,7 @@ describe('useTripPlanner road trip: saving a hit', () => { // Both the via and the new stop are projected onto the day's current routed line, // so the comparison is "which one does the car reach first" rather than a // straight-line guess. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.insertIndexFor.mockReturnValue(1) rt.routes.days = [{ dayId: 5, @@ -648,8 +649,7 @@ describe('useTripPlanner road trip: saving a hit', () => { it('FE-TP-ROAD-058: saving through the full form still lands the stop where it will be driven past', async () => { // The slice has taken a position all along and nothing ever passed it, so a place // added by way of the full form went to the end of the day. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.insertIndexFor.mockReturnValue(2) const { result } = await renderRoadtrip() @@ -662,8 +662,7 @@ describe('useTripPlanner road trip: saving a hit', () => { }) it('FE-TP-ROAD-059: a day link that fails is said out loud, and the place itself stands', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() actions.assignPlaceToDay.mockRejectedValue(new Error('day gone')) @@ -676,8 +675,7 @@ describe('useTripPlanner road trip: saving a hit', () => { }) it('FE-TP-ROAD-016: with nothing open there is nothing to warn about', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() expect(result.current.stopDraftDuplicate).toBeNull() @@ -885,8 +883,7 @@ describe('useTripPlanner road trip: one stop at a time', () => { it('FE-TP-ROAD-032: a reader may not set a stay', async () => { asReader('place_edit') - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const { result } = await renderRoadtrip() await act(async () => { await result.current.setRoadtripStay(1011, 45) }) @@ -914,8 +911,7 @@ describe('useTripPlanner road trip: other ways of driving a leg', () => { const DETOUR: [number, number][] = [[53.55, 9.99], [53.4, 11.5], [53.3, 12.8], [52.52, 13.4]] const routedDay = () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -1483,8 +1479,7 @@ describe('useTripPlanner road trip: shaping the drive on the map', () => { it('FE-TP-ROAD-050: a reader may not shape the drive at all', async () => { asReader('day_edit') - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, stops: [{ lat: 53.55, lng: 9.99 }, { lat: 52.52, lng: 13.4 }], geometry: [[53.55, 9.99], [52.52, 13.4]] }] const { result } = await renderRoadtrip() @@ -1514,8 +1509,7 @@ describe('useTripPlanner road trip: shaping the drive on the map', () => { // pulled beyond the stop it used to precede kept claiming the earlier leg: the route // ran out to the point and back before carrying on, which reads as the drag doing // nothing at all. Three stops so there are two legs to land between. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, stops: [{ lat: 53.55, lng: 9.99 }, { lat: 53.85, lng: 11.45 }, { lat: 52.52, lng: 13.4 }], @@ -1551,8 +1545,7 @@ describe('useTripPlanner road trip: shaping the drive on the map', () => { // No distance guard on a drag, unlike on a click: pulling a via well away from the // current road is the whole point of the gesture. A day with no line to measure // against simply sends no anchor, and the existing pin stays as it was. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, stops: [], geometry: [] }] const { result } = await renderRoadtrip() @@ -1576,8 +1569,7 @@ describe('useTripPlanner road trip: shaping the drive on the map', () => { describe('useTripPlanner road trip: dropping a hit where it belongs', () => { const corridorWithHit = () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.visible = [poi({ osm_id: 'node/7' })] rt.corridor.search.spine = [ { lat: 53.55, lng: 9.99 }, @@ -1638,8 +1630,7 @@ describe('useTripPlanner road trip: dropping a hit where it belongs', () => { it('FE-TP-ROAD-056: a reader may not drop one at all', async () => { asReader('place_edit') - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.visible = [poi({ osm_id: 'node/7' })] rt.corridor.search.spine = [{ lat: 53.55, lng: 9.99 }, { lat: 52.52, lng: 13.4 }] const { result } = await renderRoadtrip() @@ -1792,8 +1783,7 @@ describe('useTripPlanner road trip: a stop added by hand', () => { }) it('FE-TP-ROAD-084: a stop dropped into a routed day re-anchors its vias before it re-routes', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -2821,8 +2811,7 @@ describe('useTripPlanner road trip: a booked night in the popup', () => { describe('useTripPlanner road trip: a hit handed to the full form', () => { it('FE-TP-ROAD-107: saving it through the form re-anchors the vias the way the popup does', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.corridor.insertIndexFor.mockReturnValue(1) rt.routes.days = [{ dayId: 5, @@ -2881,8 +2870,7 @@ describe('useTripPlanner road trip: a ride on the drive (#2428)', () => { carrier: { reservationId: 70, type: 'flight', role, title: 'LH 2020', code: role === 'departure' ? 'HAM' : 'MUC', at: null }, }) const flownDay = () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -3142,8 +3130,7 @@ describe('useTripPlanner road trip: a booked night at the edge of a day', () => * on one straight road, so every leg is a stretch of the line of its own. */ const transferDay = () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -3209,8 +3196,7 @@ describe('useTripPlanner road trip: a booked night at the edge of a day', () => }) it('FE-TP-ROAD-145: behind a terminal, and on the road after the next place, a place lands where it fell', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() const carrier = (role: 'departure' | 'arrival') => ({ reservationId: 70, type: 'flight', role, title: 'LH 2020', code: null, at: null }) rt.routes.days = [{ @@ -3262,8 +3248,7 @@ describe('useTripPlanner road trip: a booked night at the edge of a day', () => it('FE-TP-ROAD-147: a way chosen for the first leg after the morning hotel is still that leg', async () => { const LINE: [number, number][] = [[53, 10], [53, 11]] - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -3386,8 +3371,7 @@ describe('useTripPlanner road trip: a booked night at the edge of a day', () => // Out of the hotel east to the lookout, then back west past the hotel to the falls: // the road between the hotel and the lookout is driven twice, and only the second // pass is a leg a via can be filed on. - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1, @@ -3409,8 +3393,7 @@ describe('useTripPlanner road trip: a booked night at the edge of a day', () => }) it('FE-TP-ROAD-152: a road only the hotel’s drives use, out in the morning and back at night, still takes none', async () => { - seedTrip({ days: [buildDay({ id: 5, day_number: 1 })] }) - rt.corridor.day = { dayId: 5, dayNumber: 1 } + seedSearchedDay() rt.routes.days = [{ dayId: 5, dayNumber: 1,