|
1 | 1 | import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; |
2 | 2 |
|
3 | 3 | import { |
| 4 | + formatDayAwareTimestamp, |
4 | 5 | formatElapsedDurationLabel, |
5 | 6 | formatExpiresInLabel, |
6 | 7 | formatRelativeTime, |
@@ -96,6 +97,55 @@ describe("formatExpiresInLabel", () => { |
96 | 97 | }); |
97 | 98 | }); |
98 | 99 |
|
| 100 | +describe("formatDayAwareTimestamp", () => { |
| 101 | + // Instants are built with the local-time Date constructor so the |
| 102 | + // calendar-day boundaries hold in any test timezone or locale. |
| 103 | + const iso = (y: number, monthIndex: number, d: number, h: number, mi: number) => |
| 104 | + new Date(y, monthIndex, d, h, mi).toISOString(); |
| 105 | + const now = new Date(2026, 7, 14, 12, 0).getTime(); |
| 106 | + const time = (isoDate: string) => formatShortTimestamp(isoDate, "12-hour"); |
| 107 | + |
| 108 | + it("shows time only for today", () => { |
| 109 | + const messageAt = iso(2026, 7, 14, 9, 30); |
| 110 | + expect(formatDayAwareTimestamp(messageAt, "12-hour", now)).toBe(time(messageAt)); |
| 111 | + }); |
| 112 | + |
| 113 | + it("labels the previous calendar day as yesterday even when under 24h old", () => { |
| 114 | + const messageAt = iso(2026, 7, 13, 23, 30); |
| 115 | + const justPastMidnight = new Date(2026, 7, 14, 0, 30).getTime(); |
| 116 | + expect(formatDayAwareTimestamp(messageAt, "12-hour", justPastMidnight)).toBe( |
| 117 | + `yesterday at ${time(messageAt)}`, |
| 118 | + ); |
| 119 | + }); |
| 120 | + |
| 121 | + it("prefixes older same-year messages with the numeric date", () => { |
| 122 | + const messageAt = iso(2026, 7, 12, 12, 34); |
| 123 | + const datePart = new Intl.DateTimeFormat(undefined, { |
| 124 | + month: "numeric", |
| 125 | + day: "numeric", |
| 126 | + }).format(new Date(messageAt)); |
| 127 | + expect(formatDayAwareTimestamp(messageAt, "12-hour", now)).toBe( |
| 128 | + `${datePart} ${time(messageAt)}`, |
| 129 | + ); |
| 130 | + }); |
| 131 | + |
| 132 | + it("includes the year once the calendar year differs", () => { |
| 133 | + const messageAt = iso(2025, 11, 31, 18, 0); |
| 134 | + const datePart = new Intl.DateTimeFormat(undefined, { |
| 135 | + month: "numeric", |
| 136 | + day: "numeric", |
| 137 | + year: "numeric", |
| 138 | + }).format(new Date(messageAt)); |
| 139 | + expect(formatDayAwareTimestamp(messageAt, "12-hour", now)).toBe( |
| 140 | + `${datePart} ${time(messageAt)}`, |
| 141 | + ); |
| 142 | + }); |
| 143 | + |
| 144 | + it("returns an empty string for invalid input", () => { |
| 145 | + expect(formatDayAwareTimestamp("not-a-date", "12-hour", now)).toBe(""); |
| 146 | + }); |
| 147 | +}); |
| 148 | + |
99 | 149 | describe("invalid timestamp inputs", () => { |
100 | 150 | it("returns an empty timestamp instead of throwing", () => { |
101 | 151 | expect(() => formatTimestamp("not-a-date", "12-hour")).not.toThrow(); |
|
0 commit comments