|
@@ -103,6 +103,7 @@ function send(over: Record<string, unknown> = {}) {
|
|
|
beforeEach(() => {
|
|
beforeEach(() => {
|
|
|
vi.clearAllMocks()
|
|
vi.clearAllMocks()
|
|
|
settings.bookingMode = 'direct'
|
|
settings.bookingMode = 'direct'
|
|
|
|
|
+ settings.timeZone = 'Europe/Oslo'
|
|
|
appSetting.findUnique.mockResolvedValue(null)
|
|
appSetting.findUnique.mockResolvedValue(null)
|
|
|
serviceRecord.create.mockResolvedValue({ id: 'rec-1', vehicleId: 'v-1' })
|
|
serviceRecord.create.mockResolvedValue({ id: 'rec-1', vehicleId: 'v-1' })
|
|
|
serviceRequest.create.mockResolvedValue({ id: 'req-1' })
|
|
serviceRequest.create.mockResolvedValue({ id: 'req-1' })
|
|
@@ -152,6 +153,37 @@ describe('booking link', () => {
|
|
|
expect(confirmation.frequency).toBe('once')
|
|
expect(confirmation.frequency).toBe('once')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ it('tells the desk and the customer the booked time on the workshop clock, not the server clock', async () => {
|
|
|
|
|
+ const slot = new Date('2026-12-02T07:00:00Z')
|
|
|
|
|
+ const format = (timeZone?: string) =>
|
|
|
|
|
+ new Intl.DateTimeFormat(undefined, {
|
|
|
|
|
+ dateStyle: 'medium',
|
|
|
|
|
+ timeStyle: 'short',
|
|
|
|
|
+ timeZone,
|
|
|
|
|
+ }).format(slot)
|
|
|
|
|
+ // A workshop whose clock reads differently from the machine running the
|
|
|
|
|
+ // tests, wherever that is: no two of these agree at this hour, so at
|
|
|
|
|
+ // least one is not the host's, and the test never passes by coincidence.
|
|
|
|
|
+ const workshopZone = ['Pacific/Auckland', 'America/Los_Angeles', 'Asia/Kolkata'].find(
|
|
|
|
|
+ (zone) => format(zone) !== format()
|
|
|
|
|
+ ) as string
|
|
|
|
|
+ settings.timeZone = workshopZone
|
|
|
|
|
+ inspectionReminderSend.findUnique.mockResolvedValue(
|
|
|
|
|
+ send({ dueAt: new Date('2026-12-20T00:00:00Z'), expiresAt: new Date('2026-12-30T00:00:00Z') })
|
|
|
|
|
+ )
|
|
|
|
|
+ vi.setSystemTime(new Date('2026-11-20T00:00:00Z'))
|
|
|
|
|
+ try {
|
|
|
|
|
+ await confirmBooking({ token: TOKEN, start: slot.toISOString() })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ vi.useRealTimers()
|
|
|
|
|
+ }
|
|
|
|
|
+ const onWorkshopClock = format(workshopZone)
|
|
|
|
|
+ expect(format()).not.toBe(onWorkshopClock)
|
|
|
|
|
+ const [notice] = notify.mock.calls as unknown as [{ message: string }][]
|
|
|
|
|
+ expect(notice[0].message).toContain(onWorkshopClock)
|
|
|
|
|
+ expect(scheduledMessage.create.mock.calls[0][0].data.body).toContain(onWorkshopClock)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
it('refuses a slot that was taken in the meantime', async () => {
|
|
it('refuses a slot that was taken in the meantime', async () => {
|
|
|
inspectionReminderSend.findUnique.mockResolvedValue(send())
|
|
inspectionReminderSend.findUnique.mockResolvedValue(send())
|
|
|
claimableResource.mockResolvedValue(null)
|
|
claimableResource.mockResolvedValue(null)
|