feat: add digital clock input and time converter integration
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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(<DigitalClock enabled={true} />);
|
||||
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(
|
||||
<DigitalClock
|
||||
enabled={false}
|
||||
currentTime={new Date(2025,0,1,14,30,15)}
|
||||
onTimeChange={onTimeChangeMock}
|
||||
/>,
|
||||
);
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders the digital time and input when enabled', () => {
|
||||
render(
|
||||
<DigitalClock
|
||||
enabled={true}
|
||||
currentTime={new Date(2025,0,1,14,30,15)}
|
||||
onTimeChange={onTimeChangeMock}
|
||||
/>,
|
||||
);
|
||||
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(
|
||||
<DigitalClock
|
||||
enabled={true}
|
||||
currentTime={new Date(2025,0,1,14,30,15)}
|
||||
onTimeChange={onTimeChangeMock}
|
||||
/>,
|
||||
);
|
||||
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(
|
||||
<DigitalClock
|
||||
enabled={true}
|
||||
currentTime={new Date(2025,0,1,14,30,15)}
|
||||
onTimeChange={onTimeChangeMock}
|
||||
/>,
|
||||
);
|
||||
const input = screen.getByTestId('time-input');
|
||||
fireEvent.change(input, { target: { value: 'notatime' } });
|
||||
expect(onTimeChangeMock).not.toHaveBeenCalled();
|
||||
expect(input).toHaveValue('notatime');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 }) => (
|
||||
<div data-testid="berlin-clock">{enabled ? 'enabled' : 'disabled'}</div>
|
||||
),
|
||||
}));
|
||||
vi.mock('../containers/DigitalClock', () => ({
|
||||
DigitalClock: ({ enabled, currentTime, onTimeChange }: { enabled: boolean; currentTime: Date; onTimeChange: (d: Date) => void }) => (
|
||||
<div data-testid="digital-clock-wrapper">
|
||||
<span data-testid="digital-clock-time">{currentTime.toISOString()}</span>
|
||||
<button data-testid="set-time" onClick={() => onTimeChange(new Date(2025,0,1,12,0,0))}>
|
||||
Set time
|
||||
</button>
|
||||
{!enabled && <span data-testid="disabled-indicator">disabled</span>}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const mockedUseCurrentTime = vi.mocked(useCurrentTime);
|
||||
|
||||
describe('TimeConverter', () => {
|
||||
it('renders the toggle checkbox', () => {
|
||||
render(<TimeConverter />);
|
||||
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(<TimeConverter />);
|
||||
// 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(<TimeConverter />);
|
||||
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(<TimeConverter />);
|
||||
fireEvent.click(screen.getByTestId('set-time'));
|
||||
expect(setCurrentTimeMock).toHaveBeenCalledWith(new Date(2025, 0, 1, 12, 0, 0));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value);
|
||||
const date = digitalToDate(value);
|
||||
if (!isNaN(date.getTime())) {
|
||||
onTimeChange(date);
|
||||
}
|
||||
};
|
||||
|
||||
if (!enabled) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span data-testid="digital-clock">{dateToDigital(currentTime)}</span>
|
||||
<input
|
||||
data-testid="time-input"
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
placeholder="HH:MM:SS"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
data-testid="use-system-time"
|
||||
checked={useSystemTime}
|
||||
onChange={(e) => setUseSystemTime(e.target.checked)}
|
||||
/>
|
||||
Use system time
|
||||
</label>
|
||||
<BerlinClock enabled={!useSystemTime} />
|
||||
<DigitalClock
|
||||
enabled={!useSystemTime}
|
||||
currentTime={currentTime}
|
||||
onTimeChange={handleTimeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user