mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
3238fbc517
The PDF export routed the whole trip under an 8 s deadline while the router is asked one leg at a time, a second apart. A cold month-long trip got about seven legs routed per export; the rest printed as straight lines at "0 m" under a total that looked final, and every further export routed a few more days from the device cache. The export now waits for every leg, bounded by the request pacing, the rate-limit retries and a per-leg timeout. The export dialog and the phone sheet count the legs while they route and offer to print without the rest. Any leg left without a road is marked in the document: the day reads "not routed" or "at least ...", the total says it is a floor, and a note names how many legs are missing. The planner overview shares the same wording. The PDF also routes what the planner map routes now: the road trip's vias, the multi-day tour stages and the reader's distance unit. Closes #2876
129 lines
6.1 KiB
TypeScript
129 lines
6.1 KiB
TypeScript
import { render, screen, fireEvent } from '@testing-library/react'
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
import { TripRouteOverviewPill, TripRouteOverviewPanel } from './TripRouteOverview'
|
|
import type { TripRouteOverview } from './useTripRouteOverview'
|
|
|
|
const overview = (over: Partial<TripRouteOverview> = {}): TripRouteOverview => ({
|
|
days: [
|
|
{ dayId: 1, dayNumber: 1, date: '2025-06-01', title: null, color: { line: '#0a84ff', casing: '#0a5cc2' }, lines: [], segments: [], distance: 82000, duration: 3600, modes: ['driving'] },
|
|
{ dayId: 2, dayNumber: 2, date: '2025-06-02', title: 'Coast road', color: { line: '#ff9f0a', casing: '#c2740a' }, lines: [], segments: [], distance: 140500, duration: 7200, modes: ['driving', 'walking'] },
|
|
],
|
|
lines: [], lineColors: [], segments: [], focusPoints: [],
|
|
totalDistance: 222500, totalDuration: 10800, loading: false,
|
|
...over,
|
|
})
|
|
|
|
describe('TripRouteOverviewPill', () => {
|
|
it('FE-MAP-TROU-001: says which way it will take you, and reports its state', () => {
|
|
const onToggle = vi.fn()
|
|
const { rerender } = render(<TripRouteOverviewPill active={false} onToggle={onToggle} />)
|
|
|
|
const button = screen.getByTestId('trip-overview-pill')
|
|
expect(button).toHaveAttribute('aria-pressed', 'false')
|
|
expect(button).toHaveAttribute('aria-label', 'map.overview.show')
|
|
|
|
fireEvent.click(button)
|
|
expect(onToggle).toHaveBeenCalledTimes(1)
|
|
|
|
rerender(<TripRouteOverviewPill active onToggle={onToggle} />)
|
|
expect(screen.getByTestId('trip-overview-pill')).toHaveAttribute('aria-label', 'map.overview.hide')
|
|
})
|
|
})
|
|
|
|
describe('TripRouteOverviewPanel', () => {
|
|
it('FE-MAP-TROU-002: leads with the trip total in the reader\'s own unit', () => {
|
|
const { rerender } = render(<TripRouteOverviewPanel overview={overview()} unit="metric" />)
|
|
expect(screen.getByText('222.5 km')).toBeInTheDocument()
|
|
|
|
rerender(<TripRouteOverviewPanel overview={overview()} unit="imperial" />)
|
|
expect(screen.getByText('138.3 mi')).toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-MAP-TROU-003: names every day, its distance and how it is travelled', () => {
|
|
render(<TripRouteOverviewPanel overview={overview()} unit="metric" />)
|
|
|
|
expect(screen.getByText('dayplan.dayN')).toBeInTheDocument()
|
|
// A day with a title is named by it rather than by its number.
|
|
expect(screen.getByText('Coast road')).toBeInTheDocument()
|
|
expect(screen.getByText('82 km')).toBeInTheDocument()
|
|
expect(screen.getByText('140.5 km')).toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-MAP-TROU-004: marks the total as partial while legs are still coming in', () => {
|
|
const { container, rerender } = render(<TripRouteOverviewPanel overview={overview({ loading: true })} unit="metric" />)
|
|
expect(container.textContent).toContain('…')
|
|
|
|
rerender(<TripRouteOverviewPanel overview={overview()} unit="metric" />)
|
|
expect(container.textContent).not.toContain('…')
|
|
})
|
|
|
|
it('FE-MAP-TROU-005: a day row picks that day when the shell offers it', () => {
|
|
const onSelectDay = vi.fn()
|
|
render(<TripRouteOverviewPanel overview={overview()} unit="metric" selectedDayId={2} onSelectDay={onSelectDay} />)
|
|
|
|
fireEvent.click(screen.getByText('Coast road'))
|
|
expect(onSelectDay).toHaveBeenCalledWith(2)
|
|
})
|
|
|
|
it('FE-MAP-TROU-006: read-only shells get rows, not buttons', () => {
|
|
render(<TripRouteOverviewPanel overview={overview()} unit="metric" />)
|
|
expect(screen.queryAllByRole('button')).toHaveLength(0)
|
|
})
|
|
|
|
it('FE-MAP-TROU-008: hugs its rows, and takes the cap the shell gives it', () => {
|
|
const { container, rerender } = render(<TripRouteOverviewPanel overview={overview()} unit="metric" />)
|
|
const card = () => screen.getByTestId('trip-overview-panel')
|
|
|
|
expect(card().style.width).toBe('fit-content')
|
|
expect(card().style.maxWidth).toBe('320px')
|
|
|
|
// The phone shell asks for a narrower card so the map keeps the space.
|
|
rerender(<TripRouteOverviewPanel overview={overview()} unit="metric" maxWidth={240} />)
|
|
expect(card().style.maxWidth).toBe('240px')
|
|
expect(container).not.toBeEmptyDOMElement()
|
|
})
|
|
|
|
it('FE-MAP-TROU-007: draws nothing at all for a trip with no routed day', () => {
|
|
const { container } = render(<TripRouteOverviewPanel overview={overview({ days: [], totalDistance: 0 })} unit="metric" />)
|
|
expect(container).toBeEmptyDOMElement()
|
|
})
|
|
|
|
it('FE-MAP-TROU-009: says when legs could not be routed, and marks the day that is short', () => {
|
|
// A refused leg stays a straight line and adds nothing to the sum, so a total shown
|
|
// without a word about it reads as the whole trip when it is not.
|
|
const days = overview().days
|
|
const short = overview({
|
|
days: [{ ...days[0], unroutedLegs: 1 }, { ...days[1], unroutedLegs: 0 }],
|
|
unroutedLegs: 1,
|
|
})
|
|
render(<TripRouteOverviewPanel overview={short} unit="metric" />)
|
|
|
|
expect(screen.getByTestId('trip-overview-unrouted')).toHaveTextContent('map.overview.unrouted')
|
|
expect(screen.getAllByRole('img', { name: 'map.overview.dayUnrouted' })).toHaveLength(1)
|
|
// The short figures read as floors, the trip's and the day's, not as measurements (#2876).
|
|
expect(screen.getAllByText('map.overview.distanceAtLeast')).toHaveLength(2)
|
|
expect(screen.getByText('140.5 km')).toBeInTheDocument()
|
|
})
|
|
|
|
it('FE-MAP-TROU-010: does not call a leg unrouted while the round is still running', () => {
|
|
// Every leg still waiting is unrouted too; the ellipsis already says the number grows.
|
|
const days = overview().days
|
|
const running = overview({
|
|
days: [{ ...days[0], unroutedLegs: 1 }, { ...days[1], unroutedLegs: 1 }],
|
|
unroutedLegs: 2,
|
|
loading: true,
|
|
})
|
|
render(<TripRouteOverviewPanel overview={running} unit="metric" />)
|
|
|
|
expect(screen.queryByTestId('trip-overview-unrouted')).toBeNull()
|
|
expect(screen.queryByRole('img', { name: 'map.overview.dayUnrouted' })).toBeNull()
|
|
})
|
|
|
|
it('FE-MAP-TROU-011: a fully routed trip carries no warning at all', () => {
|
|
render(<TripRouteOverviewPanel overview={overview({ unroutedLegs: 0 })} unit="metric" />)
|
|
|
|
expect(screen.queryByTestId('trip-overview-unrouted')).toBeNull()
|
|
expect(screen.queryByRole('img', { name: 'map.overview.dayUnrouted' })).toBeNull()
|
|
})
|
|
})
|