refactor(format): hoist the relative-time tiers and lock their invariants
Review follow-ups on the tier-shift fix. The table moves to a module-level RELATIVE_UNITS: it is constant, so rebuilding it per call was pure garbage on a path that runs once per rendered timestamp across a virtualized feed. Its ascending order is load-bearing and was unstated — sorting it largest-first would make every timestamp under a year read "just now", silently, the same shape as the bug this branch fixes. It is now documented and asserted, and the suite derives its tier coverage and translation checks from the table itself rather than from its own case list, so a new tier cannot slip in uncovered. The audit log read the empty string for an unreadable date as a value and rendered a blank cell; it now uses the `|| "—"` idiom the channel table already uses, so missing and unreadable dates look alike.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { relativeTime, relativeFromMs } from "./format";
|
||||
import { RELATIVE_UNITS, relativeTime, relativeFromMs } from "./format";
|
||||
import en from "../i18n/locales/en/time.json";
|
||||
import hu from "../i18n/locales/hu/time.json";
|
||||
|
||||
@@ -72,8 +72,27 @@ describe("relativeFromMs", () => {
|
||||
expect(relativeFromMs(NaN)).toBe("");
|
||||
});
|
||||
|
||||
it("translates every label these cases expect, in both languages", () => {
|
||||
const keys = [...new Set(CASES.map(([, , label]) => label.split(":")[0].replace("time.", "")))];
|
||||
// The three below are derived from RELATIVE_UNITS, not from CASES, so adding a tier to the
|
||||
// table without covering it here fails rather than slipping through unnoticed.
|
||||
it("keeps the unit table sorted ascending", () => {
|
||||
const divisors = RELATIVE_UNITS.map(([secs]) => secs);
|
||||
expect(divisors, "row 0 doubles as the just-now threshold").toEqual(
|
||||
[...divisors].sort((a, b) => a - b),
|
||||
);
|
||||
expect(new Set(divisors).size, "duplicate divisors would make a tier unreachable").toBe(
|
||||
divisors.length,
|
||||
);
|
||||
});
|
||||
|
||||
it("exercises every tier in the unit table", () => {
|
||||
const covered = new Set(CASES.map(([, , label]) => label.split(":")[0]));
|
||||
for (const [, key] of RELATIVE_UNITS) {
|
||||
expect(covered, `no case covers ${key}`).toContain(key);
|
||||
}
|
||||
});
|
||||
|
||||
it("translates every label the table can emit, in both languages", () => {
|
||||
const keys = ["justNow", ...RELATIVE_UNITS.map(([, key]) => key.replace("time.", ""))];
|
||||
for (const key of keys) {
|
||||
expect(en, `en/time.json is missing ${key}`).toHaveProperty(key);
|
||||
expect(hu, `hu/time.json is missing ${key}`).toHaveProperty(key);
|
||||
|
||||
Reference in New Issue
Block a user