fix(FN-1388): display Claude session reset time in usage dialog

- Fix usage dialog to show Claude session reset time when session is paused
- Add unit tests for reset time display logic in usage calculations
- Update usage summary and history components to include reset time
This commit is contained in:
gsxdsm
2026-04-09 13:43:41 -07:00
parent 5b9ad7db90
commit 4ef5d73ba1
2 changed files with 109 additions and 6 deletions

View File

@@ -851,6 +851,93 @@ describe("usage", () => {
expect(claude.windows).toHaveLength(0);
});
it("parses session window when API uses 'session' key instead of 'five_hour'", async () => {
setupClaudeMocks({
credFileContent: {
accessToken: "test-token",
scopes: ["user:profile"],
subscriptionType: "pro",
},
});
// API response uses 'session' key instead of 'five_hour'
setupClaudeApiResponse({
session: {
utilization: 35.0,
resets_at: new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString(),
},
seven_day: {
utilization: 20.0,
resets_at: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
},
});
const providers = await fetchAllProviderUsage();
const claude = providers.find((p) => p.name === "Claude")!;
expect(claude.status).toBe("ok");
expect(claude.windows).toHaveLength(2);
// Verify session window is correctly parsed
const sessionWindow = claude.windows.find((w) => w.label.includes("Session"));
expect(sessionWindow).toBeDefined();
expect(sessionWindow!.percentUsed).toBe(35);
expect(sessionWindow!.percentLeft).toBe(65);
expect(sessionWindow!.resetText).toContain("resets in");
expect(sessionWindow!.resetAt).toBeDefined();
});
it("parses utilization from alternative field names", async () => {
setupClaudeMocks({
credFileContent: {
accessToken: "test-token",
scopes: ["user:profile"],
subscriptionType: "pro",
},
});
// Test various field name variations
setupClaudeApiResponse({
five_hour: {
percent_used: 42.0,
resets_at: new Date(Date.now() + 3 * 60 * 60 * 1000).toISOString(),
},
});
const providers = await fetchAllProviderUsage();
const claude = providers.find((p) => p.name === "Claude")!;
expect(claude.status).toBe("ok");
const sessionWindow = claude.windows.find((w) => w.label.includes("Session"))!;
expect(sessionWindow.percentUsed).toBe(42);
});
it("parses reset_at from alternative field names", async () => {
setupClaudeMocks({
credFileContent: {
accessToken: "test-token",
scopes: ["user:profile"],
subscriptionType: "pro",
},
});
// Test various reset time field name variations
setupClaudeApiResponse({
five_hour: {
utilization: 30.0,
reset_at: new Date(Date.now() + 4 * 60 * 60 * 1000).toISOString(),
},
});
const providers = await fetchAllProviderUsage();
const claude = providers.find((p) => p.name === "Claude")!;
expect(claude.status).toBe("ok");
const sessionWindow = claude.windows.find((w) => w.label.includes("Session"))!;
expect(sessionWindow.resetAt).toBeDefined();
expect(sessionWindow.resetText).toContain("resets in");
});
it("preserves plan detection from subscriptionType in credentials", async () => {
setupClaudeMocks({
credFileContent: {