mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
fix(tours): the last review findings of the city walks
A place closing at midnight is no longer read as open past it: the hours run on only where the next date opens at midnight (any window of the day counts, as written order is kept), and an impossible date such as 2026-02-31 reads as unknown instead of rolling into March. The hours source names the trip's places rather than "your own" entries, in all 27 locales, as the pill beside it does. The day a walk's arrivals are checked for shows its date on the pill, not only "Day N". The 12 hour clock is tested for a window to midnight. The along-the-tour term table is built once again, now that the one test that stubbed the places client without its real exports spreads them. New test seeding goes through the ORM factories.
This commit is contained in:
@@ -192,7 +192,7 @@ describe('TourDetailDialog', () => {
|
||||
}
|
||||
const view = render(<TourDetailDialog {...defaultProps} tour={walk} days={days} assignments={assignments} selectedDayId={7} />)
|
||||
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, start 09:00')
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, Mon, Oct 19(, 2026)?, start 09:00$/)
|
||||
expect(screen.queryByTestId('walk-day-picker')).not.toBeInTheDocument()
|
||||
expect(await screen.findByText('Arrival about 09:00, stay until 09:30')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('walk-station-conflict'))
|
||||
@@ -200,7 +200,7 @@ describe('TourDetailDialog', () => {
|
||||
expect(screen.getByText('Mark visited')).toBeInTheDocument()
|
||||
|
||||
view.rerender(<TourDetailDialog {...defaultProps} tour={walk} days={days} assignments={assignments} />)
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent('Day 1, start 09:00')
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent(/^Day 1, Mon, Oct 19(, 2026)?, start 09:00$/)
|
||||
expect(screen.queryByText('Mark visited')).not.toBeInTheDocument()
|
||||
fireEvent.click(screen.getByTestId('walk-day-picker'))
|
||||
fireEvent.click(screen.getByRole('button', { name: /^Day 2, .*start 11:00$/ }))
|
||||
|
||||
@@ -294,7 +294,7 @@ describe('TourWaypointList', () => {
|
||||
|
||||
it('FE-TOUR-WAYPOINT-LIST-009: names the day above the stops and the conflict at the stop it is about', () => {
|
||||
render(<TourWaypointList planner={walkPlanner()} canEdit isSaving={false} />);
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, start 17:20');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, Mon, Oct 19(, 2026)?, start 17:20$/);
|
||||
const rows = screen.getAllByTestId('tour-planner-waypoint-row');
|
||||
const first = within(at(rows, 0)).getByTestId('walk-station-schedule');
|
||||
expect(within(first).getByText('Arrival about 17:20, stay until 18:20')).toBeInTheDocument();
|
||||
@@ -312,7 +312,7 @@ describe('TourWaypointList', () => {
|
||||
|
||||
it('FE-TOUR-WAYPOINT-LIST-010: shows no arrival while the route is not planned for the points as they are', () => {
|
||||
render(<TourWaypointList planner={walkPlanner({ routeCurrent: false })} canEdit isSaving={false} />);
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, start 17:20');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, Mon, Oct 19(, 2026)?, start 17:20$/);
|
||||
expect(screen.queryByTestId('walk-station-schedule')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('walk-station-conflict')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -36,12 +36,16 @@ describe('StationScheduleLine', () => {
|
||||
|
||||
it('follows the 12 hour format, a window to midnight included', () => {
|
||||
seedStore(useSettingsStore, { settings: { time_format: '12h' } });
|
||||
line(at({ leave: 1130, fit: { kind: 'closes', source: 'place', window: [600, 1080] } }));
|
||||
const { unmount } = line(at({ leave: 1130, fit: { kind: 'closes', source: 'place', window: [600, 1080] } }));
|
||||
expect(screen.getByText('Arrival about 5:20 PM, stay until 6:50 PM')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('walk-station-conflict')).toHaveTextContent(
|
||||
'Lenbachhaus closes at 6:00 PM, during the stay from 5:20 PM to 6:50 PM (open 10:00 AM to 6:00 PM)'
|
||||
);
|
||||
expect(screen.getByTestId('walk-hours-source')).toHaveTextContent('Trip place');
|
||||
// In 12 hours a window to midnight ends at 12:00 AM, never at 24:00.
|
||||
unmount();
|
||||
line(at({ fit: { kind: 'open', source: 'place', window: [600, 1440] } }));
|
||||
expect(screen.getByText(/Open 10:00 AM to 12:00 AM/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the window an open station is checked against; one to midnight reads 24:00', () => {
|
||||
|
||||
@@ -44,7 +44,8 @@ describe('WalkDayLine', () => {
|
||||
render(<Picker />);
|
||||
expect(screen.getByText('Times for')).toBeInTheDocument();
|
||||
const picker = screen.getByTestId('walk-day-picker');
|
||||
expect(picker).toHaveTextContent('Day 2, start 09:30');
|
||||
// The pill names the date the hours are for, as the menu does.
|
||||
expect(picker).toHaveTextContent(/^Day 2, Tue, Oct 20(, 2026)?, start 09:30$/);
|
||||
expect(picker).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
fireEvent.click(picker);
|
||||
@@ -56,7 +57,9 @@ describe('WalkDayLine', () => {
|
||||
]);
|
||||
expect(items.map((item) => !!item.querySelector('svg'))).toEqual([false, true]);
|
||||
fireEvent.click(screen.getByRole('button', { name: /^Day 1, / }));
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent('Day 1, no start time');
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent(
|
||||
/^Day 1, Mon, Oct 19(, 2026)?: Old town, no start time$/
|
||||
);
|
||||
});
|
||||
|
||||
it('closes the menu again from its pill', () => {
|
||||
@@ -72,7 +75,7 @@ describe('WalkDayLine', () => {
|
||||
const { rerender } = render(
|
||||
<WalkDayLine choice={choice({ fixed: true })} schedule={schedule('ready')} stationCount={3} />
|
||||
);
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 2, start 09:30');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 2, Tue, Oct 20(, 2026)?, start 09:30$/);
|
||||
expect(screen.queryByTestId('walk-day-picker')).not.toBeInTheDocument();
|
||||
rerender(
|
||||
<WalkDayLine
|
||||
@@ -81,7 +84,7 @@ describe('WalkDayLine', () => {
|
||||
stationCount={3}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, no start time');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, .+: Old town, no start time$/);
|
||||
expect(screen.getByText('Give the walk a time on this day to see arrivals and opening hours.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -102,7 +105,7 @@ describe('WalkDayLine', () => {
|
||||
const { rerender, container } = render(
|
||||
<WalkDayLine choice={choice()} schedule={schedule('ready', true)} stationCount={3} />
|
||||
);
|
||||
expect(screen.getByText(/Opening hours from OpenStreetMap or your own place entries/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Opening hours from OpenStreetMap or the trip's places/)).toBeInTheDocument();
|
||||
rerender(<WalkDayLine choice={choice()} schedule={schedule('noRoute', true)} stationCount={3} />);
|
||||
expect(screen.queryByText(/Opening hours from OpenStreetMap/)).not.toBeInTheDocument();
|
||||
rerender(<WalkDayLine enabled={false} choice={choice()} schedule={schedule('ready', true)} stationCount={3} />);
|
||||
|
||||
@@ -15,7 +15,7 @@ type Translate = ReturnType<typeof useTranslation>['t'];
|
||||
const PILL =
|
||||
'inline-flex min-w-0 items-center gap-1 rounded-full bg-surface-card px-2 py-0.5 text-start font-medium text-content-secondary shadow-sm';
|
||||
|
||||
/** "Day 2, start 09:00", or that it has no start time; `day` names the day as the place needs it. */
|
||||
/** "Day 2, Tue, Jul 8, start 09:00", or that it has no start time; `day` names the day with its date. */
|
||||
function optionLabel(option: WalkDayOption, day: string, t: Translate, is12h: boolean): string {
|
||||
return option.start
|
||||
? t('tours.walk.day.start', { day, time: formatClockTime(option.start, is12h) })
|
||||
@@ -30,12 +30,15 @@ function missingNote(state: WalkScheduleState, choice: WalkDayChoice, stationCou
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The day as a pill: a menu of the walk's days where there is a choice, its name alone where there is none. */
|
||||
/**
|
||||
* The day as a pill: a menu of the walk's days where there is a choice, its name alone where there is none.
|
||||
* It names the day with its date as the menu does, so the opening hours below read for a day that is in view.
|
||||
*/
|
||||
function DayPill({ choice, day }: { choice: WalkDayChoice; day: WalkDayOption }) {
|
||||
const { t, locale } = useTranslation();
|
||||
const is12h = useSettingsStore((state) => state.settings.time_format) === '12h';
|
||||
const menu = useContextMenu();
|
||||
const label = optionLabel(day, t('dayplan.dayN', { n: day.index + 1 }), t, is12h);
|
||||
const label = optionLabel(day, dayMenuLabel(day.day, day.index, t, locale), t, is12h);
|
||||
if (choice.fixed || choice.options.length < 2) {
|
||||
return (
|
||||
<span data-testid="walk-day" className={`${PILL} break-words`} style={fs(11, 'body')}>
|
||||
|
||||
@@ -206,7 +206,7 @@ describe('WalkStationsCard', () => {
|
||||
|
||||
it('on the day it was opened from, shows each arrival, the end of each stay and what the opening hours say', async () => {
|
||||
card(onMonday());
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, start 09:00');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, .+, start 09:00$/);
|
||||
expect(screen.queryByTestId('walk-day-picker')).not.toBeInTheDocument();
|
||||
const museum = await rowOf('Museum');
|
||||
expect(within(museum).getByText('Arrival about 09:00, stay until 09:30')).toBeInTheDocument();
|
||||
@@ -224,7 +224,7 @@ describe('WalkStationsCard', () => {
|
||||
within(cafe).getByText('Opening hours not checked: a stay before this stop is not entered yet.')
|
||||
).toBeInTheDocument();
|
||||
expect(within(cafe).queryByTestId('walk-station-conflict')).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/Opening hours from OpenStreetMap or your own place entries/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Opening hours from OpenStreetMap or the trip's places/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("names a stop by the trip place it stands for and checks the place's own hours: 17:20 to 18:20 against 18:00", async () => {
|
||||
@@ -259,7 +259,7 @@ describe('WalkStationsCard', () => {
|
||||
seedStore(useSettingsStore, { settings: { time_format: '12h' } });
|
||||
card(onMonday());
|
||||
expect(within(await rowOf('Museum')).getByText('Arrival about 9:00 AM, stay until 9:30 AM')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, start 9:00 AM');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, .+, start 9:00 AM$/);
|
||||
});
|
||||
|
||||
it('on a day without a start, asks for one and leaves arrivals and hours unsaid', async () => {
|
||||
@@ -267,7 +267,7 @@ describe('WalkStationsCard', () => {
|
||||
expect(
|
||||
await screen.findByText('Give the walk a time on this day to see arrivals and opening hours.')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent('Day 1, no start time');
|
||||
expect(screen.getByTestId('walk-day')).toHaveTextContent(/^Day 1, .+, no start time$/);
|
||||
await screen.findAllByTestId('walk-station');
|
||||
expect(screen.queryByText(/Arrival/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Opening hours from OpenStreetMap/)).not.toBeInTheDocument();
|
||||
@@ -306,7 +306,7 @@ describe('WalkStationsCard', () => {
|
||||
route: ROUTE,
|
||||
});
|
||||
const picker = screen.getByTestId('walk-day-picker');
|
||||
expect(picker).toHaveTextContent('Day 2, start 10:00');
|
||||
expect(picker).toHaveTextContent(/^Day 2, .+, start 10:00$/);
|
||||
// Tuesday from 10:00: the museum closed at 09:20 already.
|
||||
expect(within(await rowOf('Museum')).getByTestId('walk-station-conflict')).toHaveTextContent(
|
||||
'Museum is closed at 10:00, the stay would last until 10:30; open 09:00 to 09:20'
|
||||
@@ -319,7 +319,7 @@ describe('WalkStationsCard', () => {
|
||||
fireEvent.click(picker);
|
||||
const monday = screen.getByRole('button', { name: /^Day 1, .*no start time$/ });
|
||||
fireEvent.click(monday);
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent('Day 1, no start time');
|
||||
expect(screen.getByTestId('walk-day-picker')).toHaveTextContent(/^Day 1, .+, no start time$/);
|
||||
expect(screen.getByText('Give the walk a time on this day to see arrivals and opening hours.')).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Arrival/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user