diff --git a/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx b/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx new file mode 100644 index 0000000000000..63ddfef8adad3 --- /dev/null +++ b/apps/web/src/components/chat/ThreadDetailsUsage.browser.test.tsx @@ -0,0 +1,60 @@ +import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts"; +import { expect, it, vi } from "vite-plus/test"; + +import { act, fakeTimers, render } from "../../../test/render"; +import { ThreadDetailsUsage } from "./ThreadDetailsUsage"; + +const NOW = "2026-11-03T12:00:00.000Z"; +const instanceId = ProviderInstanceId.make("codex"); + +function codexResettingAt(resetsAt: string): ServerProvider { + return { + instanceId, + driver: ProviderDriverKind.make("codex"), + enabled: true, + installed: true, + version: "0.164.0", + status: "ready", + auth: { status: "authenticated" }, + checkedAt: NOW, + models: [], + slashCommands: [], + skills: [], + usageLimits: { + checkedAt: NOW, + windows: [ + { + id: "session", + kind: "session", + label: "5h", + usedPercent: 40, + resetsAt, + windowDurationMins: 300, + }, + ], + }, + }; +} + +it.each([ + ["2026-11-03T12:05:00.000Z", "Resets in 5m · "], + ["2026-11-03T12:00:30.000Z", "Resets momentarily · "], +])("labels a limit resetting at %s", async (resetsAt, label) => { + using _timers = fakeTimers(); + vi.setSystemTime(new Date(NOW)); + await using screen = await render( + , + ); + + await act(async () => { + screen.getByLabelText(/^5h:/).element().focus(); + await vi.advanceTimersByTimeAsync(1_000); + }); + await vi.waitFor(() => + expect(document.querySelector('[data-slot="tooltip-popup"]')?.textContent).toContain(label), + ); +}); diff --git a/apps/web/src/components/chat/ThreadDetailsUsage.tsx b/apps/web/src/components/chat/ThreadDetailsUsage.tsx index 05211d6ace44e..7dba650e80525 100644 --- a/apps/web/src/components/chat/ThreadDetailsUsage.tsx +++ b/apps/web/src/components/chat/ThreadDetailsUsage.tsx @@ -6,7 +6,7 @@ import type { } from "@t3tools/contracts"; import { collectProviderUsageLimits, - formatDuration, + formatResetsIn, type LimitPace, limitsNotice, paceOf, @@ -162,7 +162,7 @@ function LimitRow({ {window.resetsAt && resetsAt > now ? ( - Resets in {formatDuration(resetsAt - now)} ·{" "} + {formatResetsIn(window, now)?.replace("resets in", "Resets in")} ·{" "} {formatUpcomingTimestamp(window.resetsAt, timestampFormat, now)} ) : null}