| ... | @@ -8,37 +8,9 @@ import { | ... | @@ -8,37 +8,9 @@ import { |
| 8 | formatRelativeTimeLabel, | 8 | formatRelativeTimeLabel, |
| 9 | formatShortTimestamp, | 9 | formatShortTimestamp, |
| 10 | getRelativeTimeState, | 10 | getRelativeTimeState, |
| 11 | getTimestampFormatOptions, | | |
| 12 | resolveTimestampLocale, | 11 | resolveTimestampLocale, |
| 13 | } from "./timestampFormat"; | 12 | } from "./timestampFormat"; |
| 14 | | 13 | |
| 15 | describe("getTimestampFormatOptions", () => { | | |
| 16 | it("omits hour12 when locale formatting is requested", () => { | | |
| 17 | expect(getTimestampFormatOptions("locale", true)).toEqual({ | | |
| 18 | hour: "numeric", | | |
| 19 | minute: "2-digit", | | |
| 20 | second: "2-digit", | | |
| 21 | }); | | |
| 22 | }); | | |
| 23 | | | |
| 24 | it("builds a 12-hour formatter with seconds when requested", () => { | | |
| 25 | expect(getTimestampFormatOptions("12-hour", true)).toEqual({ | | |
| 26 | hour: "numeric", | | |
| 27 | minute: "2-digit", | | |
| 28 | second: "2-digit", | | |
| 29 | hour12: true, | | |
| 30 | }); | | |
| 31 | }); | | |
| 32 | | | |
| 33 | it("builds a 24-hour formatter without seconds when requested", () => { | | |
| 34 | expect(getTimestampFormatOptions("24-hour", false)).toEqual({ | | |
| 35 | hour: "numeric", | | |
| 36 | minute: "2-digit", | | |
| 37 | hour12: false, | | |
| 38 | }); | | |
| 39 | }); | | |
| 40 | }); | | |
| 41 | | | |
| 42 | describe("resolveTimestampLocale", () => { | 14 | describe("resolveTimestampLocale", () => { |
| 43 | it("defers to the runtime default when the host reports no locale", () => { | 15 | it("defers to the runtime default when the host reports no locale", () => { |
| 44 | expect(resolveTimestampLocale(null)).toBeUndefined(); | 16 | expect(resolveTimestampLocale(null)).toBeUndefined(); |
| ... | @@ -57,19 +29,26 @@ describe("resolveTimestampLocale", () => { | ... | @@ -57,19 +29,26 @@ describe("resolveTimestampLocale", () => { |
| 57 | expect(resolveTimestampLocale("not a locale")).toBeUndefined(); | 29 | expect(resolveTimestampLocale("not a locale")).toBeUndefined(); |
| 58 | expect(resolveTimestampLocale("en_GB")).toBeUndefined(); | 30 | expect(resolveTimestampLocale("en_GB")).toBeUndefined(); |
| 59 | }); | 31 | }); |
| | 32 | }); |
| 60 | | 33 | |
| 61 | it("renders the host locale's hour cycle under the locale setting", () => { | 34 | describe("formatShortTimestamp", () => { |
| 62 | const formatAt1544 = (systemLocale: string | null) => | 35 | afterEach(() => { |
| 63 | new Intl.DateTimeFormat(resolveTimestampLocale(systemLocale), { | 36 | vi.unstubAllGlobals(); |
| 64 | ...getTimestampFormatOptions("locale", false), | 37 | vi.resetModules(); |
| 65 | timeZone: "UTC", | 38 | }); |
| 66 | }) | | |
| 67 | .format(new Date("2026-04-07T15:44:00.000Z")) | | |
| 68 | // ICU separates the day period with a narrow no-break space. | | |
| 69 | .replace(/[  ]/g, " "); | | |
| 70 | | 39 | |
| 71 | expect(formatAt1544("en-GB")).toBe("15:44"); | 40 | it.each([ |
| 72 | expect(formatAt1544("en-US")).toBe("3:44 PM"); | 41 | ["en-GB", "15:44"], |
| | 42 | ["en-US", "3:44 PM"], |
| | 43 | ])("honors %s and the explicit hour-cycle settings", async (locale, localTime) => { |
| | 44 | vi.stubGlobal("window", { desktopBridge: { getSystemLocale: () => locale } }); |
| | 45 | vi.resetModules(); |
| | 46 | const { formatShortTimestamp: format } = await import("./timestampFormat"); |
| | 47 | const date = new Date(2026, 3, 7, 15, 44).toISOString(); |
| | 48 | // ICU can separate the day period with a narrow no-break space. |
| | 49 | expect(format(date, "locale").replace(/[  ]/g, " ")).toBe(localTime); |
| | 50 | expect(format(date, "12-hour").replace(/[  ]/g, " ")).toMatch(/^3:44 [ap]m$/i); |
| | 51 | expect(format(date, "24-hour")).toBe("15:44"); |
| 73 | }); | 52 | }); |
| 74 | }); | 53 | }); |
| 75 | | 54 | |