109 lines
4.3 KiB
TypeScript
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} />);
|
|
});
|
|
});
|