import { page } from 'vitest/browser'; import { describe, it, expect, vi } from 'vitest'; import { render } from 'vitest-browser-svelte'; const { submitRef } = vi.hoisted(() => ({ submitRef: { current: null as unknown as (opts: { formData: FormData; cancel?: () => void }) => unknown } })); vi.mock('$app/forms', () => ({ enhance: (_node: unknown, submit: unknown) => { submitRef.current = submit as typeof submitRef.current; return () => {}; } })); import BlockModal from './BlockModal.svelte'; const categories = [{ id: 1, name: 'Physical' }]; const groups = [{ id: 1, name: 'Prayers' }]; const proclamations = [ { id: 'p1', name: 'Peace', categoryId: 1, groupId: 1 }, { id: 'p2', name: 'Strength', categoryId: 1, groupId: 1 } ]; function mount(overrides: Record = {}) { return render(BlockModal, { open: true, mode: 'create', categories, groups, proclamations, existingBlocks: [], initialStartMinutes: 6 * 60, initialEndMinutes: 8 * 60, initialProclamationIds: ['p1'], onClose: () => {}, onSaved: () => {}, ...overrides }); } function submit() { const form = document.querySelector('form') as HTMLFormElement; return submitRef.current({ formData: new FormData(form), cancel: () => {} }); } describe('BlockModal', () => { it('uses separate hour and minute selects for start and end', async () => { mount(); await expect.element(page.getByRole('combobox', { name: /^Start hour$/ })).toBeInTheDocument(); await expect .element(page.getByRole('combobox', { name: /^Start minute$/ })) .toBeInTheDocument(); await expect.element(page.getByRole('combobox', { name: /^End hour$/ })).toBeInTheDocument(); await expect.element(page.getByRole('combobox', { name: /^End minute$/ })).toBeInTheDocument(); }); it('pre-fills the selects from the initial times', async () => { mount({ initialStartMinutes: 7 * 60 + 30, initialEndMinutes: 9 * 60 + 15 }); await expect.element(page.getByRole('combobox', { name: /^Start hour$/ })).toHaveValue('7'); await expect.element(page.getByRole('combobox', { name: /^Start minute$/ })).toHaveValue('30'); await expect.element(page.getByRole('combobox', { name: /^End hour$/ })).toHaveValue('9'); await expect.element(page.getByRole('combobox', { name: /^End minute$/ })).toHaveValue('15'); }); it('shows the review times and selected proclamation titles', async () => { mount(); await expect.element(page.getByText('6:00 AM – 8:00 AM')).toBeInTheDocument(); await expect.element(page.getByText('Peace')).toBeInTheDocument(); }); it('marks an overnight block in the review', async () => { mount({ initialStartMinutes: 21 * 60, initialEndMinutes: 9 * 60 }); await expect.element(page.getByText('9:00 PM – 9:00 AM (next day)')).toBeInTheDocument(); }); it('rejects an overlapping time range with the specific error', async () => { mount({ existingBlocks: [{ id: 'b1', startMinutes: 6 * 60, endMinutes: 8 * 60 }], initialStartMinutes: 7 * 60, initialEndMinutes: 9 * 60 }); await page.getByRole('combobox', { name: /^Start hour$/ }).element(); submit(); await expect .element(page.getByText(/This overlaps with your 6:00 AM–8:00 AM block\./)) .toBeInTheDocument(); }); it('requires at least one proclamation', async () => { mount({ initialProclamationIds: [] }); await page.getByRole('combobox', { name: /^Start hour$/ }).element(); submit(); await expect.element(page.getByText('Select at least one proclamation.')).toBeInTheDocument(); }); it('does not show an error for a valid selection', async () => { mount(); await page.getByRole('combobox', { name: /^Start hour$/ }).element(); const result = submit(); expect(typeof result).toBe('function'); await expect.element(page.getByRole('alert')).not.toBeInTheDocument(); }); });