import { describe, expect, test } from "bun:test"; import { renderToStaticMarkup } from "react-dom/server"; import { clampPercent, resetPhrase, usageAccessibleText, usageLevel, usageValueText, UsageMeter, UsageMeterGroup, UsageReadout, usageResets, } from "./usage-meter"; describe("usage levels", () => { test("a status tone only below 10% left", () => { expect(usageLevel(22)).toBe("healthy"); expect(usageLevel(10)).toBe("healthy"); expect(usageLevel(9.9)).toBe("low"); expect(usageLevel(8)).toBe("low"); expect(usageLevel(0)).toBe("exhausted"); expect(usageLevel(-3)).toBe("exhausted"); }); test("a missing reading is unknown, never zero", () => { expect(usageLevel(null)).toBe("unknown"); expect(usageLevel(undefined)).toBe("unknown"); expect(usageLevel(Number.NaN)).toBe("unknown"); expect(usageValueText(null)).toBe("Not reported yet"); }); test("machines measure what's used, so the tone flips near full", () => { expect(usageLevel(32, "used")).toBe("healthy"); expect(usageLevel(94, "used")).toBe("low"); expect(usageLevel(100, "used")).toBe("exhausted"); expect(usageValueText(32, "used")).toBe("32% used"); }); test("values are clamped and rounded, and exhausted says so", () => { expect(clampPercent(21.6)).toBe(22); expect(clampPercent(140)).toBe(100); expect(clampPercent(-5)).toBe(0); expect(usageValueText(21.6)).toBe("22% left"); expect(usageValueText(0)).toBe("Limit reached"); }); test("the accessible text names the window, the value and the reset", () => { expect( usageAccessibleText({ label: "Weekly", percent: 22, resetsLabel: "Mon 28 Sep, 09:00" }), ).toBe("Weekly: 22% left, resets Mon 28 Sep, 09:00"); expect(usageAccessibleText({ label: "5-hour", percent: 64 })).toBe("5-hour: 64% left"); }); test("relative days read lowercase after Resets, dates stay as they are", () => { expect(resetPhrase("Today, 17:10")).toBe("today, 17:10"); expect(resetPhrase("Tomorrow, 09:00")).toBe("tomorrow, 09:00"); expect(resetPhrase("Mon 28 Sep, 09:00")).toBe("Mon 28 Sep, 09:00"); expect(usageAccessibleText({ label: "5-hour", percent: 64, resetsLabel: "Today, 17:10" })).toBe( "5-hour: 64% left, resets today, 17:10", ); expect( renderToStaticMarkup(), ).toContain("Resets today, 17:10"); }); }); describe("UsageMeter", () => { test("loading reserves the reset-time line instead of moving later sections down", () => { const html = renderToStaticMarkup(); expect(html).toContain('aria-busy="true"'); expect(html).toContain("h-4.5 w-32"); expect(html).not.toContain("Not reported yet"); }); test("a known reading is a meter with a text value", () => { const html = renderToStaticMarkup( , ); expect(html).toContain('role="meter"'); expect(html).toContain('aria-valuenow="22"'); expect(html).toContain('aria-valuetext="Weekly: 22% left, resets Mon 28 Sep, 09:00"'); expect(html).toContain("Resets Mon 28 Sep, 09:00"); expect(html).toContain("width:22%"); }); test("an exhausted reading draws no fill and keeps the reset time", () => { const html = renderToStaticMarkup( , ); expect(html).toContain('data-level="exhausted"'); expect(html).toContain("Limit reached"); expect(html).toContain("width:0%"); expect(html).toContain("Resets Mon 28 Sep, 09:00"); }); test("an unknown reading is not announced as a meter", () => { const html = renderToStaticMarkup(); expect(html).not.toContain('role="meter"'); expect(html).toContain('role="group"'); expect(html).toContain("Not reported yet"); }); test("labels are never abbreviated in any look or density", () => { for (const variant of ["bar", "text", "ring"] as const) { for (const density of ["full", "compact"] as const) { const html = renderToStaticMarkup( , ); expect(html).toContain("5-hour"); expect(html).not.toMatch(/>5h<|>Wk { const html = renderToStaticMarkup( , ); expect(html).toContain("22% left"); expect(html).not.toContain("width:"); expect(html).toContain('role="meter"'); }); test("compact meters are phrasing content, so they fit inside a row's text", () => { const html = renderToStaticMarkup( , ); expect(html).not.toContain(" { const html = renderToStaticMarkup( undefined} refreshDisabledReason="Reconnect research@acme.dev to check its usage." />, ); expect(html).toContain("Checked 4 min ago"); expect(html).toContain('aria-label="Check usage now"'); expect(html).toContain('aria-disabled="true"'); expect(html).toContain("Reconnect research@acme.dev to check its usage."); }); }); describe("UsageReadout", () => { // Thursday 15 October 2026, 10:00 UTC. const NOW = Date.parse("2026-10-15T10:00:00Z"); const weeklyReset = "2026-10-19T07:00:00Z"; const fiveHourReset = "2026-10-15T13:20:00Z"; test("reads as a sentence with a small bar, and names the reset for screen readers", () => { const html = renderToStaticMarkup( , ); expect(html).toContain("78% left this week"); expect(html).toContain('role="meter"'); expect(html).toMatch(/aria-valuetext="78% left this week\. Resets Mon 19 Oct, \d\d:\d\d"/); expect(html).toContain("w-16"); }); test("shows when each limit resets, the shown window first and the others by name", () => { const html = renderToStaticMarkup( , ); expect(html).toContain('data-slot="usage-readout-resets"'); expect(html).toMatch(/>Resets Mon \d\d:\d\d · 5-hour \d\d:\d\d { const html = renderToStaticMarkup( , ); expect(html).toContain("Limit reached"); expect(html).toMatch(/>5-hour resets \d\d:\d\dResets /); }); test("without reset times there is no second line", () => { const html = renderToStaticMarkup(); expect(html).not.toContain("usage-readout-resets"); expect(html).not.toContain("title="); }); test("usageResets puts the shown window first and drops a reset in the same minute", () => { expect( usageResets( [ { label: "Weekly", percent: 40, resetsAt: Date.parse(weeklyReset) }, { label: "5-hour", percent: 90, resetsAt: Date.parse(fiveHourReset) }, { label: "Weekly · Opus", percent: 70, resetsAt: Date.parse(weeklyReset) }, { label: "Extra usage", percent: null }, ], "5-hour", ), ).toEqual([ { at: Date.parse(fiveHourReset) }, { label: "Weekly", at: Date.parse(weeklyReset) }, ]); }); test("a missing reading says why in words, and an empty window says so", () => { expect( renderToStaticMarkup( , ), ).toContain("Usage unavailable"); expect(renderToStaticMarkup()).toContain( "Limit reached", ); }); });