import { describe, expect, it } from "vitest"; import { compactRelativeTime, formatCount, formatDate, formatDateTime, formatDuration, relativeTime } from "./format"; describe("formatCount", () => { it("leaves counts under a thousand alone", () => { expect(formatCount(0)).toBe("0"); expect(formatCount(1)).toBe("1"); expect(formatCount(999)).toBe("999"); }); it("abbreviates thousands, dropping a trailing zero decimal", () => { expect(formatCount(1000)).toBe("1k"); expect(formatCount(1200)).toBe("1.2k"); expect(formatCount(12_345)).toBe("12.3k"); expect(formatCount(999_000)).toBe("999k"); }); it("truncates instead of rounding, so a count never overflows its unit", () => { expect(formatCount(1299)).toBe("1.2k"); expect(formatCount(999_999)).toBe("999.9k"); expect(formatCount(1_999_999)).toBe("1.9m"); }); it("abbreviates millions", () => { expect(formatCount(1_000_000)).toBe("1m"); expect(formatCount(2_500_000)).toBe("2.5m"); expect(formatCount(1_200_000_000)).toBe("1200m"); }); it("renders nothing for a missing count", () => { expect(formatCount(undefined)).toBe(""); expect(formatCount(null)).toBe(""); }); }); describe("formatDuration", () => { it("reads 0s under a second, so an instant run differs from one that never ran", () => { expect(formatDuration(0)).toBe("0s"); expect(formatDuration(999)).toBe("0s"); }); it("joins only the non-zero chunks", () => { expect(formatDuration(45_000)).toBe("45s"); expect(formatDuration(90_000)).toBe("1m 30s"); expect(formatDuration(3_600_000)).toBe("1h"); expect(formatDuration(90_061_000)).toBe("1d 1h 1m 1s"); }); it("rejects negative and non-finite input", () => { expect(formatDuration(-1)).toBe(""); expect(formatDuration(Number.NaN)).toBe(""); expect(formatDuration(Number.POSITIVE_INFINITY)).toBe(""); }); }); describe("relativeTime", () => { const now = new Date("2026-08-18T12:00:00Z"); it("describes the past and the future", () => { expect(relativeTime("2026-08-18T11:59:30Z", now)).toBe("30 seconds ago"); expect(relativeTime("2026-08-18T09:00:00Z", now)).toBe("3 hours ago"); expect(relativeTime("2026-08-15T12:00:00Z", now)).toBe("3 days ago"); expect(relativeTime("2026-08-18T14:00:00Z", now)).toBe("in 2 hours"); }); it("falls back to years past the largest division", () => { expect(relativeTime("2020-08-18T12:00:00Z", now)).toBe("6 years ago"); }); it("accepts a Date and renders nothing for an unparseable string", () => { expect(relativeTime(new Date("2026-08-17T12:00:00Z"), now)).toBe("yesterday"); expect(relativeTime("not a date", now)).toBe(""); }); }); describe("compactRelativeTime", () => { const now = new Date("2026-08-18T12:00:00Z"); it("truncates to the largest whole unit that fits", () => { expect(compactRelativeTime("2026-08-18T11:59:59Z", now)).toBe("1s ago"); expect(compactRelativeTime("2026-08-18T11:30:00Z", now)).toBe("30m ago"); expect(compactRelativeTime("2026-08-18T02:00:00Z", now)).toBe("10h ago"); expect(compactRelativeTime("2026-08-15T12:00:00Z", now)).toBe("3d ago"); expect(compactRelativeTime("2026-08-01T12:00:00Z", now)).toBe("2w ago"); expect(compactRelativeTime("2026-05-18T12:00:00Z", now)).toBe("3mo ago"); expect(compactRelativeTime("2020-08-18T12:00:00Z", now)).toBe("6y ago"); }); it("flips the suffix for future timestamps", () => { expect(compactRelativeTime("2026-08-18T12:00:30Z", now)).toBe("30s from now"); expect(compactRelativeTime("2026-08-20T12:00:00Z", now)).toBe("2d from now"); }); it("renders nothing for an unparseable string", () => { expect(compactRelativeTime("not a date", now)).toBe(""); }); }); describe("formatDate", () => { it("renders a short month-day-year", () => { expect(formatDate(new Date(2026, 7, 18, 12))).toBe("Aug 18, 2026"); }); it("renders nothing for an unparseable string", () => { expect(formatDate("not a date")).toBe(""); }); }); describe("formatDateTime", () => { it("appends a time and zone to the date", () => { // zone abbreviation depends on the runner's timezone, so match the shape expect(formatDateTime(new Date(2026, 7, 18, 15, 4))).toMatch(/^Aug 18, 2026, 3:04 PM .+$/); }); it("renders nothing for an unparseable string", () => { expect(formatDateTime("not a date")).toBe(""); }); });