Files
babymonitor/frontend/tests/unit/time.test.ts
T

197 lines
5.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
formatMinutesAfter,
toLocalDatetimeInputValue,
formatElapsedTime,
clampToDay,
parseTime,
formatMinutes,
getToday,
getDaysAgo,
timeToMinutes,
adjustForTimezone,
getLocalOffsetFromReference,
formatDateTime,
toUTCFromLocalInput,
parseBackendDateTime,
} from '../../src/utils/time';
describe('formatMinutesAfter', () => {
it('converts positive minutes after reference', () => {
expect(formatMinutesAfter(75, '04:00')).toBe('05:15');
});
it('handles zero minutes', () => {
expect(formatMinutesAfter(0, '04:00')).toBe('04:00');
});
it('wraps around midnight for large values', () => {
expect(formatMinutesAfter(480, '04:00')).toBe('12:00');
});
it('handles null input', () => {
expect(formatMinutesAfter(null, '04:00')).toBe('N/A');
});
it('handles negative minutes', () => {
expect(formatMinutesAfter(-30, '04:00')).toBe('03:30');
});
});
describe('toLocalDatetimeInputValue', () => {
it('converts Date to datetime-local format', () => {
const date = new Date('2024-01-15T21:30:00Z');
const result = toLocalDatetimeInputValue(date);
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
});
});
describe('formatElapsedTime', () => {
it('formats hours and minutes', () => {
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
expect(formatElapsedTime(twoHoursAgo)).toBe('2h 0m');
});
it('formats minutes only', () => {
const thirtyMinsAgo = new Date(Date.now() - 30 * 60 * 1000).toISOString();
expect(formatElapsedTime(thirtyMinsAgo)).toBe('30m');
});
});
describe('clampToDay', () => {
it('clamps to start of day', () => {
const date = new Date('2024-01-15T15:30:00');
const result = clampToDay(date, 'start');
expect(result.getHours()).toBe(0);
expect(result.getMinutes()).toBe(0);
});
it('clamps to end of day', () => {
const date = new Date('2024-01-15T15:30:00');
const result = clampToDay(date, 'end');
expect(result.getHours()).toBe(23);
expect(result.getMinutes()).toBe(59);
});
});
describe('parseTime', () => {
it('parses HH:MM string to Date', () => {
const result = parseTime('07:30');
expect(result.getHours()).toBe(7);
expect(result.getMinutes()).toBe(30);
});
});
describe('formatMinutes', () => {
it('formats hours and minutes', () => {
expect(formatMinutes(90)).toBe('1h 30m');
});
it('formats hours only', () => {
expect(formatMinutes(120)).toBe('2h 0m');
});
it('formats minutes only', () => {
expect(formatMinutes(45)).toBe('45m');
});
});
describe('getDaysAgo', () => {
it('returns date string for N days ago', () => {
const result = getDaysAgo(7);
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
});
});
describe('getToday', () => {
it('returns today date string', () => {
const result = getToday();
const today = new Date().toISOString().split('T')[0];
expect(result).toBe(today);
});
});
describe('timeToMinutes', () => {
it('converts HH:MM to minutes', () => {
expect(timeToMinutes('07:30')).toBe(450);
expect(timeToMinutes('00:00')).toBe(0);
expect(timeToMinutes('23:59')).toBe(1439);
});
});
describe('adjustForTimezone', () => {
it('adjusts minutes by the local timezone offset', () => {
const utcMins = 600; // 10:00 UTC
const adjusted = adjustForTimezone(utcMins);
const offset = new Date().getTimezoneOffset();
expect(adjusted).toBe(utcMins - offset);
});
});
describe('formatDateTime', () => {
it('treats string without Z as UTC', () => {
const dateStr = '2024-01-15T10:00:00';
const dateWithZ = '2024-01-15T10:00:00Z';
expect(formatDateTime(dateStr)).toBe(formatDateTime(dateWithZ));
});
it('formats a Date object correctly', () => {
const date = new Date('2024-01-15T10:00:00Z');
// The output depends on the local timezone, so we check for part of it
const result = formatDateTime(date);
expect(result).toMatch(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/);
});
});
describe('getLocalOffsetFromReference', () => {
it('returns local offset from reference in minutes', () => {
const utcMins = 600; // 10:00 UTC
const refTime = '07:00'; // 07:00 reference
const offset = getLocalOffsetFromReference(utcMins, refTime);
const localMins = adjustForTimezone(utcMins);
const refMins = timeToMinutes(refTime);
expect(offset).toBe(localMins - refMins);
});
});
describe('toUTCFromLocalInput', () => {
it('converts valid datetime-local string to ISO UTC string', () => {
const input = '2024-01-15T10:00';
// This is tricky because it depends on local timezone.
// But we just want to see if it works for a valid input.
const result = toUTCFromLocalInput(input);
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/);
});
it('returns empty string on invalid datetime-local string', () => {
expect(toUTCFromLocalInput('2026-06-0')).toBe('');
});
});
describe('parseBackendDateTime', () => {
it('should parse a UTC string without Z as UTC', () => {
const time = '2026-06-04 13:58:00';
const date = parseBackendDateTime(time);
expect(date.toISOString()).toBe('2026-06-04T13:58:00.000Z');
});
it('should parse a UTC string with Z as UTC', () => {
const time = '2026-06-04T13:58:00Z';
const date = parseBackendDateTime(time);
expect(date.toISOString()).toBe('2026-06-04T13:58:00.000Z');
});
it('should parse a string with an offset correctly', () => {
const time = '2026-06-04T13:58:00+02:00';
const date = parseBackendDateTime(time);
expect(date.toISOString()).toBe('2026-06-04T11:58:00.000Z');
});
it('should handle ISO format with T', () => {
const time = '2026-06-04T13:58:00';
const date = parseBackendDateTime(time);
expect(date.toISOString()).toBe('2026-06-04T13:58:00.000Z');
});
});