Files
babymonitor/frontend/tests/unit/TimeAdjuster.test.tsx
T

109 lines
4.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import TimeAdjuster from '../../src/components/tracker/TimeAdjuster';
import { toLocalDatetimeInputValue } from '../../src/utils/time';
describe('TimeAdjuster', () => {
const MOCK_NOW = new Date('2024-06-10T12:00:00Z');
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(MOCK_NOW);
});
afterEach(() => {
vi.useRealTimers();
vi.clearAllMocks();
});
it('initializes with the current time when no defaultValue is provided', () => {
render(<TimeAdjuster onTimeChange={() => {}} />);
const input = screen.getByLabelText(/Time/i) as HTMLInputElement;
// The value should be the local format of MOCK_NOW
expect(input.value).toBe(toLocalDatetimeInputValue(MOCK_NOW));
});
it('initializes with the provided defaultValue converted to local format', () => {
const defaultValue = '2024-01-01T10:00:00Z';
const expectedLocal = toLocalDatetimeInputValue(new Date(defaultValue));
render(<TimeAdjuster onTimeChange={() => {}} defaultValue={defaultValue} />);
const input = screen.getByLabelText(/Time/i) as HTMLInputElement;
expect(input.value).toBe(expectedLocal);
});
it('calls onTimeChange with UTC time when manual value is changed via input', async () => {
const onTimeChange = vi.fn();
render(<TimeAdjuster onTimeChange={onTimeChange} />);
const input = screen.getByLabelText(/Time/i) as HTMLInputElement;
// Change to a specific local time
const newLocalTime = '2024-06-11T15:45';
fireEvent.change(input, { target: { value: newLocalTime } });
// onTimeChange should be called with the UTC equivalent
// We need to be careful about how Date(newLocalTime) interprets it.
// Without timezone, it's usually treated as local time.
const expectedUTC = new Date(newLocalTime).toISOString();
// Since onTimeChange is in a useEffect, we might need to wait or use vi.runAllTimers()
// But standard React state updates + useEffect should be synchronous enough for fireEvent in Vitest
expect(onTimeChange).toHaveBeenCalledWith(expectedUTC);
});
it('resets to current time when "Reset to now" button is clicked', () => {
const onTimeChange = vi.fn();
render(<TimeAdjuster onTimeChange={onTimeChange} />);
const input = screen.getByLabelText(/Time/i) as HTMLInputElement;
// 1. Change value to something else
const differentTime = '2023-01-01T00:00';
fireEvent.change(input, { target: { value: differentTime } });
expect(input.value).toBe(differentTime);
// 2. Click reset
const resetButton = screen.getByText(/Reset to now/i);
fireEvent.click(resetButton);
// 3. Verify input is back to MOCK_NOW's local format
expect(input.value).toBe(toLocalDatetimeInputValue(MOCK_NOW));
// 4. Verify onTimeChange was called with MOCK_NOW's UTC format
expect(onTimeChange).toHaveBeenLastCalledWith(MOCK_NOW.toISOString());
});
it('handles invalid date input gracefully by calling onTimeChange with an empty string', async () => {
const onTimeChange = vi.fn();
render(<TimeAdjuster onTimeChange={onTimeChange} />);
const input = screen.getByLabelText(/Time/i) as HTMLInputElement;
// Simulate invalid input (though datetime-local usually prevents this via UI,
// we test the logic in toUTCFromLocalInput)
fireEvent.change(input, { target: { value: 'not-a-date' } });
// Wait for useEffect
await vi.waitFor(() => {
expect(onTimeChange).toHaveBeenCalledWith("");
});
});
it('does not call onTimeChange if the input value is same as current (initialization check)', () => {
// The component has an effect on [value, onTimeChange].
// On mount, it will call onTimeChange because value is set in useState.
const onTimeChange = vi.fn();
render(<TimeAdjuster onTimeChange={onTimeChange} />);
// Should be called once on mount due to useEffect [value]
expect(onTimeChange).toHaveBeenCalledTimes(1);
expect(onTimeChange).toHaveBeenCalledWith(MOCK_NOW.toISOString());
});
it('does not call onTimeChange if it is not provided (edge case for coverage)', () => {
// @ts-ignore - testing the branch where onTimeChange might be undefined
render(<TimeAdjuster onTimeChange={undefined} />);
});
});