Files
TREK/client/src/components/Planner/useReservationForm.test.ts
T
Maurice 62a80f420c fix(bookings): an end time before the start time on the same day shows a warning in the booking dialog
A booking saved with 18:00 to 09:00 and no end date was stored as ending
earlier on its start day without a word. The dialog now says that the end
time lies before the start time and that an end date is needed if it ends
the next day, on desktop and on the phone. It only warns and does not block
the save, since time zones can make such times correct.

Refs #2604
2026-10-12 20:42:48 +02:00

106 lines
5.0 KiB
TypeScript

// FE-PLANNER-RESFORMHOOK-001 to -005: the booking form state behind the desktop
// ReservationModal and the phone's booking sheet.
import { act, renderHook } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { buildDay, buildPlace } from '../../../tests/helpers/factories';
import type { ReservationFields } from './reservationFormModel';
import { useReservationForm } from './useReservationForm';
const EMPTY: ReservationFields = {
title: '',
type: 'other',
status: 'pending',
reservation_time: '',
reservation_end_time: '',
end_date: '',
location: '',
confirmation_number: '',
notes: '',
url: '',
place_id: '',
accommodation_id: '',
meta_check_in_time: '',
meta_check_out_time: '',
hotel_place_id: '',
hotel_start_day: '',
hotel_end_day: '',
hotel_address: '',
};
const days = [buildDay({ id: 1, date: '2026-07-01' }), buildDay({ id: 2, date: '2026-07-02' })];
const places = [buildPlace({ id: 5, name: 'Castle', address: 'Hill 1' })];
describe('useReservationForm', () => {
it('FE-PLANNER-RESFORMHOOK-001: opens on the empty form, bounded by the trip, and fields follow the edits', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
expect(result.current.form).toBe(EMPTY);
expect(result.current.dateBounds).toEqual({ min: '2026-07-01', max: '2026-07-02' });
act(() => result.current.set('title', 'Tour'));
act(() => result.current.toggleTraveler(3));
expect(result.current.form.title).toBe('Tour');
expect([...result.current.travelerIds]).toEqual([3]);
act(() => result.current.toggleTraveler(3));
expect(result.current.travelerIds.size).toBe(0);
});
it('FE-PLANNER-RESFORMHOOK-002: the start date and time are set apart and the end check follows', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
act(() => result.current.setStartTime('09:00', '2026-07-01'));
expect(result.current.form.reservation_time).toBe('2026-07-01T09:00');
expect([result.current.startDate, result.current.startTime]).toEqual(['2026-07-01', '09:00']);
act(() => result.current.setStartDate('2026-07-02'));
expect(result.current.form.reservation_time).toBe('2026-07-02T09:00');
expect(result.current.isEndBeforeStart).toBe(false);
act(() => result.current.set('end_date', '2026-07-01'));
expect(result.current.isEndBeforeStart).toBe(true);
act(() => result.current.setStartDate(''));
expect(result.current.form.reservation_time).toBe('');
act(() => result.current.takeStopDay('2026-07-01'));
expect(result.current.form.reservation_time).toBe('2026-07-01');
act(() => result.current.takeStopDay('2026-07-02'));
expect(result.current.form.reservation_time).toBe('2026-07-01');
});
it('FE-PLANNER-RESFORMHOOK-003: picking a place or a hotel fills the title and the address', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
act(() => result.current.pickPlace(5));
expect(result.current.form).toMatchObject({ place_id: 5, title: 'Castle', location: 'Hill 1' });
act(() => result.current.pickHotelPlace(5));
expect(result.current.form).toMatchObject({ hotel_place_id: 5, hotel_address: 'Hill 1' });
});
it('FE-PLANNER-RESFORMHOOK-004: the stay range keeps its order in the trip', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
act(() => result.current.pickHotelStart(1));
act(() => result.current.pickHotelEnd(2));
expect(result.current.form).toMatchObject({ hotel_start_day: 1, hotel_end_day: 2 });
act(() => result.current.pickHotelStart(2));
expect(result.current.form).toMatchObject({ hotel_start_day: 2, hotel_end_day: 2 });
act(() => result.current.pickHotelEnd(1));
expect(result.current.form).toMatchObject({ hotel_start_day: 1, hotel_end_day: 1 });
});
it('FE-PLANNER-RESFORMHOOK-005: the save data is built from what the form holds now', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
act(() => result.current.set('title', 'Show'));
act(() => result.current.setStartTime('20:00', '2026-07-02'));
expect(result.current.saveData({ assignmentId: 4, isEdit: false, withCheckInEnd: false })).toMatchObject({
title: 'Show',
reservation_time: '2026-07-02T20:00',
assignment_id: 4,
endpoints: [],
});
});
it('FE-PLANNER-RESFORMHOOK-006: an end time before the start time on the same day raises the warning (#2604)', () => {
const { result } = renderHook(() => useReservationForm(EMPTY, days, places));
act(() => result.current.setStartTime('18:00', '2026-07-01'));
expect(result.current.endTimeBeforeStartTime).toBe(false);
act(() => result.current.set('reservation_end_time', '09:00'));
expect(result.current.endTimeBeforeStartTime).toBe(true);
expect(result.current.isEndBeforeStart).toBe(false);
act(() => result.current.set('end_date', '2026-07-02'));
expect(result.current.endTimeBeforeStartTime).toBe(false);
});
});