import { describe, it, expect, beforeEach, vi } from 'vitest' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { DayOverridesInput } from './DayOverridesInput' describe('DayOverridesInput', () => { const mockOverrides = [ { id: 'do-1', workspaceId: 'ws-1', date: '2026-12-24', isWorkingDay: true, note: 'Jobbade på juldagen', createdAt: '2026-01-01T00:00:00', updatedAt: '2026-01-01T00:00:00', }, { id: 'do-2', workspaceId: 'ws-1', date: '2026-06-20', isWorkingDay: false, note: null, createdAt: '2026-01-01T00:00:00', updatedAt: '2026-01-01T00:00:00', }, ] let mockOnAdd: ReturnType let mockOnRemove: ReturnType beforeEach(() => { mockOnAdd = vi.fn().mockResolvedValue(undefined) mockOnRemove = vi.fn().mockResolvedValue(undefined) }) describe('rendering', () => { it('renders list of existing overrides', () => { render( ) expect(screen.getByText('24 december 2026')).toBeInTheDocument() expect(screen.getByText('20 juni 2026')).toBeInTheDocument() }) it('renders working day override with correct status', () => { render( ) expect(screen.getByTestId('override-item-do-1').textContent).toContain('Jag jobbar den här dagen') }) it('renders off day override with correct status', () => { render( ) expect(screen.getByTestId('override-item-do-2').textContent).toContain('Jag är ledig den här dagen') }) it('renders note when present', () => { render( ) expect(screen.getByText('Anteckning: Jobbade på juldagen')).toBeInTheDocument() }) it('renders empty state when no overrides', () => { render( ) expect(screen.getByText('Du har inga undantag i kalendern ännu.')).toBeInTheDocument() }) it('renders add form with all fields', () => { render( ) expect(screen.getByTestId('override-date-input')).toBeInTheDocument() expect(screen.getByTestId('override-working-radio')).toBeInTheDocument() expect(screen.getByTestId('override-off-radio')).toBeInTheDocument() expect(screen.getByTestId('override-note-input')).toBeInTheDocument() expect(screen.getByTestId('add-override-button')).toBeInTheDocument() }) }) describe('adding override', () => { it('calls onAdd with correct payload when working day is selected', async () => { render( ) fireEvent.change(screen.getByTestId('override-date-input'), { target: { value: '2026-12-24' } }) fireEvent.click(screen.getByTestId('override-working-radio')) fireEvent.change(screen.getByTestId('override-note-input'), { target: { value: 'Jobbar denna dag' } }) fireEvent.click(screen.getByTestId('add-override-button')) await waitFor(() => { expect(mockOnAdd).toHaveBeenCalledWith({ workspaceId: 'ws-1', date: '2026-12-24', isWorkingDay: true, note: 'Jobbar denna dag', }) }) }) it('calls onAdd with correct payload when off day is selected', async () => { render( ) fireEvent.change(screen.getByTestId('override-date-input'), { target: { value: '2026-06-20' } }) fireEvent.click(screen.getByTestId('override-off-radio')) fireEvent.click(screen.getByTestId('add-override-button')) await waitFor(() => { expect(mockOnAdd).toHaveBeenCalledWith({ workspaceId: 'ws-1', date: '2026-06-20', isWorkingDay: false, note: null, }) }) }) it('clears form after successful add', async () => { render( ) const dateInput = screen.getByTestId('override-date-input') as HTMLInputElement const noteInput = screen.getByTestId('override-note-input') as HTMLInputElement fireEvent.change(dateInput, { target: { value: '2026-12-24' } }) fireEvent.change(noteInput, { target: { value: 'Test note' } }) fireEvent.click(screen.getByTestId('add-override-button')) await waitFor(() => { expect(dateInput.value).toBe('') expect(noteInput.value).toBe('') }) }) it('disables submit button when date is empty', () => { render( ) const submitButton = screen.getByTestId('add-override-button') as HTMLButtonElement expect(submitButton.disabled).toBe(true) }) it('does not call onAdd when the form is submitted with no date entered', async () => { render( ) // Submit the form directly (bypassing the disabled submit button) to // exercise the handler's own empty-date guard. fireEvent.submit(screen.getByTestId('add-override-form')) await waitFor(() => { expect(mockOnAdd).not.toHaveBeenCalled() }) }) }) describe('removing override', () => { it('calls onRemove with correct id', async () => { render( ) fireEvent.click(screen.getByTestId('remove-override-do-1')) await waitFor(() => { expect(mockOnRemove).toHaveBeenCalledWith('do-1') }) }) it('swallows an onRemove failure instead of throwing (handled by the parent)', async () => { mockOnRemove.mockRejectedValueOnce(new Error('delete failed')) render( ) fireEvent.click(screen.getByTestId('remove-override-do-1')) await waitFor(() => { expect(mockOnRemove).toHaveBeenCalledWith('do-1') }) // The item is still rendered — the component didn't crash on the rejection. expect(screen.getByTestId('override-item-do-1')).toBeInTheDocument() }) }) describe('loading state', () => { it('disables form during loading', () => { render( ) expect((screen.getByTestId('override-date-input') as HTMLInputElement).disabled).toBe(true) expect((screen.getByTestId('add-override-button') as HTMLButtonElement).disabled).toBe(true) }) }) })