diff --git a/package.json b/package.json index 1813a99..7a4df94 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "test": "vitest run src/__tests__/BerlinClock.test.tsx", + "test": "vitest run src/__tests__/*", "format": "eslint . --fix && prettier --write .", "e2e": "cypress run", "ci": "npm run format && npm run test && npm run e2e" diff --git a/src/__tests__/DigitalClock.test.tsx b/src/__tests__/DigitalClock.test.tsx index 0381d1e..20e53f5 100644 --- a/src/__tests__/DigitalClock.test.tsx +++ b/src/__tests__/DigitalClock.test.tsx @@ -1,8 +1,81 @@ -import { render } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import { DigitalClock } from '../containers/DigitalClock'; +import * as utils from '../utils'; + +vi.mock('../utils'); + +const mockedDateToDigital = vi.mocked(utils.dateToDigital); +const mockedDigitalToDate = vi.mocked(utils.digitalToDate); describe('DigitalClock', () => { - it('renders when enabled', () => { - render(); + const onTimeChangeMock = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + mockedDateToDigital.mockReturnValue('14:30:15'); + mockedDigitalToDate.mockImplementation((str: string) => { + const parts = str.split(':'); + if (parts.length !== 3) return new Date(NaN); + const [h, m, s] = parts.map(Number); + if (isNaN(h) || isNaN(m) || isNaN(s)) return new Date(NaN); + const d = new Date(2025, 0, 1, 14, 30, 15); + d.setHours(h, m, s, 0); + return d; + }); + }); + + it('renders nothing when disabled', () => { + const { container } = render( + , + ); + expect(container.innerHTML).toBe(''); + }); + + it('renders the digital time and input when enabled', () => { + render( + , + ); + expect(screen.getByTestId('digital-clock')).toHaveTextContent('14:30:15'); + expect(screen.getByTestId('time-input')).toBeInTheDocument(); + }); + + it('calls onTimeChange when a valid time is entered', () => { + render( + , + ); + const input = screen.getByTestId('time-input'); + fireEvent.change(input, { target: { value: '12:00:00' } }); + + expect(onTimeChangeMock).toHaveBeenCalledTimes(1); + const expectedDate = new Date(2025, 0, 1, 12, 0, 0, 0); + expect(onTimeChangeMock).toHaveBeenCalledWith(expectedDate); + + expect(input).toHaveValue('12:00:00'); + }); + + it('does not call onTimeChange when an invalid time is entered', () => { + render( + , + ); + const input = screen.getByTestId('time-input'); + fireEvent.change(input, { target: { value: 'notatime' } }); + expect(onTimeChangeMock).not.toHaveBeenCalled(); + expect(input).toHaveValue('notatime'); }); }); diff --git a/src/__tests__/TimeConverter.test.tsx b/src/__tests__/TimeConverter.test.tsx index cf4c3de..db0d02b 100644 --- a/src/__tests__/TimeConverter.test.tsx +++ b/src/__tests__/TimeConverter.test.tsx @@ -1,13 +1,56 @@ import { render, screen, fireEvent } from '@testing-library/react'; import { TimeConverter } from '../containers/TimeConverter'; +import { useCurrentTime } from '../hooks/useCurrentTime'; + +vi.mock('../hooks/useCurrentTime'); +vi.mock('../containers/BerlinClock', () => ({ + BerlinClock: ({ enabled }: { enabled: boolean }) => ( +
{enabled ? 'enabled' : 'disabled'}
+ ), +})); +vi.mock('../containers/DigitalClock', () => ({ + DigitalClock: ({ enabled, currentTime, onTimeChange }: { enabled: boolean; currentTime: Date; onTimeChange: (d: Date) => void }) => ( +
+ {currentTime.toISOString()} + + {!enabled && disabled} +
+ ), +})); + +const mockedUseCurrentTime = vi.mocked(useCurrentTime); describe('TimeConverter', () => { - it('renders the toggle checkbox', () => { - render(); - expect(screen.getByRole('checkbox')).toBeInTheDocument(); + const setCurrentTimeMock = vi.fn(); + const baseDate = new Date(2025, 0, 1, 14, 30, 15); + + beforeEach(() => { + vi.clearAllMocks(); + mockedUseCurrentTime.mockReturnValue({ + currentTime: baseDate, + setCurrentTime: setCurrentTimeMock, + }); }); - it('disables clocks when checkbox is checked', () => { + + it('renders the checkbox, BerlinClock, and DigitalClock', () => { render(); - // TODO + expect(screen.getByTestId('use-system-time')).toBeInTheDocument(); + expect(screen.getByTestId('berlin-clock')).toHaveTextContent('enabled'); + expect(screen.getByTestId('digital-clock-wrapper')).toBeInTheDocument(); + }); + + it('disables both clocks when checkbox is checked', () => { + render(); + fireEvent.click(screen.getByTestId('use-system-time')); + expect(screen.getByTestId('berlin-clock')).toHaveTextContent('disabled'); + expect(screen.getByTestId('disabled-indicator')).toBeInTheDocument(); + }); + + it('calls setCurrentTime when DigitalClock triggers onTimeChange', () => { + render(); + fireEvent.click(screen.getByTestId('set-time')); + expect(setCurrentTimeMock).toHaveBeenCalledWith(new Date(2025, 0, 1, 12, 0, 0)); }); }); diff --git a/src/containers/DigitalClock.tsx b/src/containers/DigitalClock.tsx index 4408719..c97c4d0 100644 --- a/src/containers/DigitalClock.tsx +++ b/src/containers/DigitalClock.tsx @@ -1,13 +1,36 @@ import { useState } from 'react'; -import { useCurrentTime } from '../hooks/useCurrentTime'; -import { dateToDigital } from '../utils'; +import { dateToDigital, digitalToDate } from '../utils'; interface DigitalClockProps { enabled: boolean; + currentTime: Date; + onTimeChange: (time: Date) => void; } -export function DigitalClock({ enabled }: DigitalClockProps) { - const { currentTime } = useCurrentTime(); - const digital = dateToDigital(currentTime); - return null; +export function DigitalClock({ enabled, currentTime, onTimeChange }: DigitalClockProps) { + const [inputValue, setInputValue] = useState(dateToDigital(currentTime)); + + const handleChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setInputValue(value); + const date = digitalToDate(value); + if (!isNaN(date.getTime())) { + onTimeChange(date); + } + }; + + if (!enabled) return null; + + return ( +
+ {dateToDigital(currentTime)} + +
+ ); } diff --git a/src/containers/TimeConverter.tsx b/src/containers/TimeConverter.tsx index 9e35a2e..d536573 100644 --- a/src/containers/TimeConverter.tsx +++ b/src/containers/TimeConverter.tsx @@ -4,7 +4,30 @@ import { DigitalClock } from './DigitalClock'; import { useCurrentTime } from '../hooks/useCurrentTime'; export function TimeConverter() { + const { currentTime, setCurrentTime } = useCurrentTime(); const [useSystemTime, setUseSystemTime] = useState(false); - // When useSystemTime is true, both BerlinClock and DigitalClock are disabled. - return null; + + const handleTimeChange = (newTime: Date) => { + setCurrentTime(newTime); + }; + + return ( +
+ + + +
+ ); }