mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
c2dd66986c
A GPX or KML import stores the track's first point as the place's position, so the day route treated it as a single stop there and routed from the track start to the next stop, a second road running alongside the track. Any assigned place with a usable track now gets the gap a Tour already had: the route runs up to the track start and continues from the track end, on the desktop map and day list, the mobile timeline, the trip overview and the PDF map, which now draws the track as a line. A place without a usable track stays a plain stop and never shows as an invalid Tour. Refs #2600
83 lines
4.6 KiB
TypeScript
83 lines
4.6 KiB
TypeScript
import { act, renderHook, waitFor } from '@testing-library/react'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { buildAssignment, buildDay, buildPlace } from '../../tests/helpers/factories'
|
|
import { resetAllStores } from '../../tests/helpers/store'
|
|
import { useAddonStore } from '../store/addonStore'
|
|
import { useSettingsStore } from '../store/settingsStore'
|
|
import { useTripStore } from '../store/tripStore'
|
|
import { useRouteCalculation } from './useRouteCalculation'
|
|
|
|
const mocks = vi.hoisted(() => ({ route: vi.fn(), mirrorServiceStops: true }))
|
|
vi.mock('../components/Map/RouteCalculator', async importActual => ({
|
|
...await importActual<typeof import('../components/Map/RouteCalculator')>(),
|
|
calculateRouteWithLegs: mocks.route,
|
|
}))
|
|
vi.mock('./useRoadtripSettings', () => ({
|
|
useRoadtripSettings: (select: (settings: { roadtrip_service_stops_in_days: boolean }) => unknown) =>
|
|
select({ roadtrip_service_stops_in_days: mocks.mirrorServiceStops }),
|
|
}))
|
|
|
|
function seed(enabled = true, excluded = false) {
|
|
const places = [
|
|
buildPlace({ id: 1, lat: 48.1, lng: 11.1 }),
|
|
buildPlace({ id: 2, lat: 48.2, lng: 11.2, route_geometry: '[[48.2,11.2],[48.3,11.3]]' }),
|
|
buildPlace({ id: 3, lat: 48.4, lng: 11.4 }),
|
|
]
|
|
const assignments = { '1': places.map((place, order) => buildAssignment({
|
|
day_id: 1, order_index: order, place,
|
|
...(order === 1 ? { tour_place_id: 2, tour_route_geometry: '[[49,12],[49.1,12.1]]', route_excluded: excluded } : {}),
|
|
})) }
|
|
useAddonStore.setState({ addons: [{ id: 'tours', name: 'Tours', icon: 'Route', type: 'feature', enabled }] })
|
|
useTripStore.setState({ days: [buildDay({ id: 1 })], assignments, places, reservations: [] })
|
|
return places
|
|
}
|
|
|
|
beforeEach(() => {
|
|
resetAllStores()
|
|
mocks.mirrorServiceStops = true
|
|
mocks.route.mockReset().mockImplementation(async (points: Array<{ lat: number; lng: number }>) => ({
|
|
coordinates: points.map(point => [point.lat, point.lng]), legs: [],
|
|
}))
|
|
useSettingsStore.setState(state => ({ settings: { ...state.settings, optimize_from_accommodation: false } }))
|
|
})
|
|
|
|
describe('B5 useRouteCalculation', () => {
|
|
it.each([
|
|
{ enabled: true, excluded: false, expected: [[48.1, 48.2], [48.3, 48.4]] },
|
|
{ enabled: true, excluded: true, expected: [[48.1, 48.4]] },
|
|
// Off, the place still has its track, so the drive breaks there all the same (#2600).
|
|
{ enabled: false, excluded: false, expected: [[48.1, 48.2], [48.3, 48.4]] },
|
|
])('routes enabled=$enabled excluded=$excluded using the shared day topology', async ({ enabled, excluded, expected }) => {
|
|
seed(enabled, excluded)
|
|
const { result } = renderHook(() => useRouteCalculation(useTripStore(), 1, true, 'walking'))
|
|
await waitFor(() => expect(result.current.route?.map(run => run.map(point => point[0]))).toEqual(expected))
|
|
expect(mocks.route.mock.calls.map(call => call[0].map((point: { lat: number }) => point.lat))).toEqual(expected)
|
|
expect(useTripStore.getState().assignments['1']).toHaveLength(3)
|
|
})
|
|
|
|
it('filters hidden service stops before the Tour projection', async () => {
|
|
mocks.mirrorServiceStops = false
|
|
const places = seed()
|
|
const service = buildAssignment({ day_id: 1, order_index: 0.5,
|
|
place: buildPlace({ id: 4, lat: 48.15, lng: 11.15, stop_type: 'fuel' }) })
|
|
useTripStore.setState(state => ({ assignments: { '1': [...state.assignments['1'], service] }, places }))
|
|
const { result } = renderHook(() => useRouteCalculation(useTripStore(), 1, true, 'walking'))
|
|
await waitFor(() => expect(result.current.route).toHaveLength(2))
|
|
expect(mocks.route.mock.calls.map(call => call[0].map((point: { lat: number }) => point.lat)))
|
|
.toEqual([[48.1, 48.2], [48.3, 48.4]])
|
|
})
|
|
|
|
it('aborts an old calculation and prevents its late answer replacing current Place geometry', async () => {
|
|
const places = seed()
|
|
let release: (answer: { coordinates: number[][]; legs: never[] }) => void = () => {}
|
|
mocks.route.mockImplementationOnce(() => new Promise(resolve => { release = resolve }))
|
|
const { result } = renderHook(() => useRouteCalculation(useTripStore(), 1, true, 'walking'))
|
|
const oldSignal = mocks.route.mock.calls[0][1].signal as AbortSignal
|
|
act(() => useTripStore.setState({ places: places.map(place => place.id === 2
|
|
? { ...place, route_geometry: '[[48.2,11.2],[48.35,11.35]]' } : place) }))
|
|
await waitFor(() => expect(result.current.route?.[1]?.[0]).toEqual([48.35, 11.35]))
|
|
expect(oldSignal.aborted).toBe(true)
|
|
await act(async () => release({ coordinates: [[49, 12], [49.1, 12.1]], legs: [] }))
|
|
expect(result.current.route?.[1]?.[0]).toEqual([48.35, 11.35])
|
|
})
|
|
}) |