63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
import { parseServerTime, relativeTime, relativeSince, absoluteLocal } from '../src/lib/time';
|
|
|
|
const NOW = Date.parse('2026-10-18T20:00:00Z');
|
|
|
|
const at = (msAgo: number) => new Date(NOW - msAgo);
|
|
|
|
describe('parseServerTime', () => {
|
|
it('parses RFC3339', () => {
|
|
expect(parseServerTime('2026-10-18T19:59:00Z')?.getTime()).toBe(
|
|
Date.parse('2026-10-18T19:59:00Z'),
|
|
);
|
|
});
|
|
|
|
it('returns null for absent or garbage values', () => {
|
|
expect(parseServerTime(undefined)).toBeNull();
|
|
expect(parseServerTime(null)).toBeNull();
|
|
expect(parseServerTime('')).toBeNull();
|
|
expect(parseServerTime('not-a-date')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('relativeTime', () => {
|
|
it('says "just now" under a second, including for clock skew', () => {
|
|
expect(relativeTime(at(0), NOW)).toBe('just now');
|
|
expect(relativeTime(at(500), NOW)).toBe('just now');
|
|
// A phone clock ahead of the server must never render "in 3 seconds".
|
|
expect(relativeTime(at(-5000), NOW)).toBe('just now');
|
|
});
|
|
|
|
it('counts seconds under a minute', () => {
|
|
expect(relativeTime(at(1000), NOW)).toBe('1s ago');
|
|
expect(relativeTime(at(2000), NOW)).toBe('2s ago');
|
|
expect(relativeTime(at(59000), NOW)).toBe('59s ago');
|
|
});
|
|
|
|
it('counts minutes, hours and days above that', () => {
|
|
expect(relativeTime(at(60000), NOW)).toBe('1m ago');
|
|
expect(relativeTime(at(13 * 60000 + 42000), NOW)).toBe('13m ago');
|
|
expect(relativeTime(at(3600000), NOW)).toBe('1h ago');
|
|
expect(relativeTime(at(47 * 3600000), NOW)).toBe('1d ago');
|
|
expect(relativeTime(at(3 * 86400000), NOW)).toBe('3d ago');
|
|
});
|
|
});
|
|
|
|
describe('relativeSince', () => {
|
|
it('formats a server string', () => {
|
|
const iso = new Date(NOW - 2000).toISOString();
|
|
expect(relativeSince(iso, NOW)).toBe('2s ago');
|
|
});
|
|
|
|
it('returns null when there is nothing to format', () => {
|
|
expect(relativeSince(undefined, NOW)).toBeNull();
|
|
expect(relativeSince('nope', NOW)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('absoluteLocal', () => {
|
|
it('renders zero-padded local hh:mm:ss', () => {
|
|
const d = new Date(2026, 9, 18, 8, 5, 3);
|
|
expect(absoluteLocal(d)).toBe('08:05:03');
|
|
});
|
|
});
|