Files
kandev/apps/web/components/settings/agent-usage-section.test.tsx
T
João Salavisa ef635276f9 feat: rename task inline from the top bar via double-click (#1702)
* feat: rename task inline from the top bar via double-click

Double-clicking the task title in the desktop top bar swaps it for an
inline input; Enter commits the rename through the existing task update
path, Escape or blur cancels. Archived tasks stay read-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test: fix vi.fn generics in agent usage test for vitest 4

vi.fn dropped the two-type-argument form in the Vitest 4 upgrade; the
old signature made mockResolvedValue infer never and fail typecheck.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: ignore Enter during IME composition in topbar rename input

Review follow-up: without the guard, the Enter that confirms an IME
candidate committed the rename mid-typing. Mirrors the quick-chat tab
rename input and the dialog default-action convention.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: harden topbar rename commit path against IME 229 and mid-edit archive

Independent-review follow-up:
- also treat keyCode 229 as IME composition, matching the documented
  convention in dialog-default-action (some IMEs report the accepting
  Enter after isComposing flips false)
- re-check isArchived on Enter so a task archived while the inline
  editor is open cannot be renamed from it

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: make topbar rename title keyboard-operable

Addresses PR #1702 review: the renameable title exposed role="link" with
aria-disabled="false" but only a double-click handler. Add tabIndex, Enter
to open the rename input, and focus restoration on keyboard exits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-15 10:48:56 +01:00

83 lines
2.7 KiB
TypeScript

import { cleanup, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { TooltipProvider } from "@kandev/ui/tooltip";
import { AgentUsageSection } from "@/components/settings/agent-usage-section";
import type { AgentSubscriptionUsageResponse } from "@/lib/types/http";
const listAgentSubscriptionUsage = vi.hoisted(() =>
vi.fn<() => Promise<AgentSubscriptionUsageResponse>>(),
);
vi.mock("@/lib/api", () => ({
listAgentSubscriptionUsage,
}));
vi.mock("@/components/agent-logo", () => ({
AgentLogo: ({ agentName }: { agentName: string }) => <span data-testid={`logo-${agentName}`} />,
}));
afterEach(() => {
cleanup();
listAgentSubscriptionUsage.mockReset();
});
describe("AgentUsageSection", () => {
it("renders nothing when no subscription agents are present", async () => {
listAgentSubscriptionUsage.mockResolvedValue({ agents: [] });
const { container } = render(<AgentUsageSection />);
await waitFor(() => expect(listAgentSubscriptionUsage).toHaveBeenCalled());
expect(container.querySelector('[data-testid="agent-usage-section"]')).toBeNull();
});
it("renders utilization windows, plan badge, and per-agent errors", async () => {
listAgentSubscriptionUsage.mockResolvedValue({
agents: [
{
agent_id: "claude-acp",
display_name: "Claude Code",
usage: {
provider: "anthropic",
plan: "max",
windows: [
{
label: "5-hour",
utilization_pct: 62,
reset_at: new Date(Date.now() + 3600_000).toISOString(),
},
{
label: "7-day",
utilization_pct: 15,
reset_at: new Date(Date.now() + 86400_000).toISOString(),
},
],
fetched_at: new Date().toISOString(),
},
},
{
agent_id: "codex-acp",
display_name: "Codex",
error: "codex usage: unexpected status 500",
},
],
});
render(
<TooltipProvider>
<AgentUsageSection />
</TooltipProvider>,
);
await screen.findByTestId("agent-usage-section");
expect(screen.getByText("Claude Code")).toBeDefined();
expect(screen.getByText("max")).toBeDefined();
expect(screen.getByText("Good")).toBeDefined(); // worst window 62% → Good
expect(screen.getByText("5h")).toBeDefined();
expect(screen.getByText("62%")).toBeDefined();
expect(screen.getByText("7d")).toBeDefined();
expect(screen.getByText("Codex")).toBeDefined();
expect(screen.getByText("Could not fetch usage data.")).toBeDefined();
});
});