import { isOpenNow, isAlwaysOpen } from '../src/hours.js'; const HOURS_24_7 = { mon: ['00:00', '24:00'], tue: ['00:00', '24:00'], wed: ['00:00', '24:00'], thu: ['00:00', '24:00'], fri: ['00:00', '24:00'], sat: ['00:00', '24:00'], sun: ['00:00', '24:00'], }; const HOURS_NORMAL_WEEK = { mon: ['09:00', '21:00'], tue: ['09:00', '21:00'], wed: ['09:00', '21:00'], thu: ['09:00', '21:00'], fri: ['09:00', '21:00'], sat: ['09:00', '14:00'], sun: null, }; const HOURS_ALL_CLOSED = { mon: null, tue: null, wed: null, thu: null, fri: null, sat: null, sun: null, }; const HOURS_MIDNIGHT_CROSS = { mon: ['22:00', '02:00'], tue: ['09:00', '21:00'], wed: null, thu: null, fri: null, sat: null, sun: null, }; const at = (iso) => new Date(iso); describe('isAlwaysOpen', () => { test('all 7 days 00:00-24:00 → true', () => { expect(isAlwaysOpen(HOURS_24_7)).toBe(true); }); test('accepts JSON string', () => { expect(isAlwaysOpen(JSON.stringify(HOURS_24_7))).toBe(true); }); test('one day different → false', () => { expect(isAlwaysOpen({ ...HOURS_24_7, sun: ['00:00', '23:59'] })).toBe(false); }); test('one day null → false', () => { expect(isAlwaysOpen({ ...HOURS_24_7, sun: null })).toBe(false); }); test('null input → false', () => { expect(isAlwaysOpen(null)).toBe(false); expect(isAlwaysOpen('')).toBe(false); expect(isAlwaysOpen(undefined)).toBe(false); }); test('malformed JSON → false', () => { expect(isAlwaysOpen('{not-json')).toBe(false); }); }); describe('isOpenNow', () => { test('null/empty input → null', () => { expect(isOpenNow(null)).toBeNull(); expect(isOpenNow('')).toBeNull(); expect(isOpenNow(undefined)).toBeNull(); }); test('malformed JSON → null', () => { expect(isOpenNow('{garbage')).toBeNull(); }); test('24/7 → { isOpen: true, kind: "24h" }', () => { const s = isOpenNow(HOURS_24_7, at('2026-07-27T10:00:00')); expect(s).toEqual({ isOpen: true, kind: '24h' }); }); test('24/7 at midnight → still 24h', () => { const s = isOpenNow(HOURS_24_7, at('2026-07-27T00:00:00')); expect(s).toEqual({ isOpen: true, kind: '24h' }); }); test('24/7 (JSON string) → kind 24h', () => { const s = isOpenNow(JSON.stringify(HOURS_24_7), at('2026-07-27T15:00:00')); expect(s).toEqual({ isOpen: true, kind: '24h' }); }); test('normal weekday at 10:00 (monday) → open, closesAt 21:00', () => { // 2026-07-27 is a Monday (verify with date -d) const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-27T10:30:00')); expect(s).toEqual({ isOpen: true, kind: 'open', closesAt: '21:00' }); }); test('normal weekday at 21:30 (monday) → closed, nextOpen tuesday 09:00', () => { const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-27T21:30:00')); expect(s.isOpen).toBe(false); expect(s.kind).toBe('after-close'); expect(s.nextOpen).toEqual({ day: 'tue', time: '09:00' }); }); test('normal weekday at 08:30 (monday) → closed before-open, opensAt 09:00', () => { const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-27T08:30:00')); expect(s).toEqual({ isOpen: false, kind: 'before-open', opensAt: '09:00', nextOpen: null }); }); test('sunday with no hours → closed, nextOpen monday 09:00', () => { // 2026-07-26 is a Sunday const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-26T10:00:00')); expect(s.isOpen).toBe(false); expect(s.kind).toBe('closed'); expect(s.nextOpen).toEqual({ day: 'mon', time: '09:00' }); }); test('all week closed → closed, no nextOpen', () => { const s = isOpenNow(HOURS_ALL_CLOSED, at('2026-07-27T10:00:00')); expect(s).toEqual({ isOpen: false, kind: 'closed', nextOpen: null }); }); test('midnight-crossing: monday 22:00-02:00, at 23:00 → open', () => { const s = isOpenNow(HOURS_MIDNIGHT_CROSS, at('2026-07-27T23:00:00')); expect(s.isOpen).toBe(true); expect(s.kind).toBe('open'); expect(s.closesAt).toBe('02:00'); }); test('midnight-crossing: monday 22:00-02:00, at 01:00 → open (yesterday range still active)', () => { // Tuesday 01:00 → it's inside monday's 22:00-02:00 range (extended past midnight) const s = isOpenNow(HOURS_MIDNIGHT_CROSS, at('2026-07-28T01:00:00')); expect(s.isOpen).toBe(true); expect(s.kind).toBe('open'); }); test('midnight-crossing: at tuesday 03:00, today opens at 09:00 → before-open', () => { // Tuesday 03:00 with monday 22:00-02:00 already finished; today's tue is 09:00-21:00. const s = isOpenNow(HOURS_MIDNIGHT_CROSS, at('2026-07-28T03:00:00')); expect(s).toEqual({ isOpen: false, kind: 'before-open', opensAt: '09:00', nextOpen: null }); }); test('midnight-crossing: same day before today opens → before-open with today time', () => { // HOURS_MIDNIGHT_CROSS: mon 22:00-02:00 (crossing). At monday 10:00, before today's 22:00. const s = isOpenNow(HOURS_MIDNIGHT_CROSS, at('2026-07-27T10:00:00')); expect(s).toEqual({ isOpen: false, kind: 'before-open', opensAt: '22:00', nextOpen: null }); }); test('saturday morning at 10:00 (with 09:00-14:00) → open, closesAt 14:00', () => { // 2026-07-25 is a Saturday const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-25T10:00:00')); expect(s).toEqual({ isOpen: true, kind: 'open', closesAt: '14:00' }); }); test('saturday at 15:00 → closed, nextOpen monday 09:00', () => { const s = isOpenNow(HOURS_NORMAL_WEEK, at('2026-07-25T15:00:00')); expect(s.isOpen).toBe(false); expect(s.kind).toBe('after-close'); expect(s.nextOpen).toEqual({ day: 'mon', time: '09:00' }); }); });