- BoardView columns now window their card list against the available
rows so the cursor stays visible. Cards beyond the viewport scroll
in via "↑ N more" / "↓ N more" indicators. Fixes the "can't reach
cards in the Done column" bug — selectedIndex was advancing but the
cards were clipped.
- Logs section: G (vim-style, in addition to End) jumps to newest
entry. Lowercase g stays as the global Settings shortcut.
- MainHeader gains two new collapse tiers: cols < 50 renders just
FUSION + the active tab pills (no inactive tabs, no dividers); rows
< 10 hides the header entirely so the row isn't wasted.
- ExpandedLog stretches to width="100%" so the panel keeps its width
when an entry is expanded — was collapsing to content width before.
- Lighter blue palette throughout (cyanBright / cyan); status-mode
grid widened to 5:6 so Stats panel gets ~45% of cols.
- Stats: heap limit always on its own continuation row; bytes
formatted with a space ("450 MB") so a forced wrap, if it ever
happens, breaks after the unit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { render, screen, within } from "@testing-library/react";
|
|
import { AgentTokenStatsPanel } from "../AgentTokenStatsPanel";
|
|
import type { Agent } from "../../api";
|
|
|
|
function makeAgent(overrides: Partial<Agent>): Agent {
|
|
return {
|
|
id: "agent-default",
|
|
name: "Default Agent",
|
|
role: "executor",
|
|
state: "idle",
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
metadata: {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("AgentTokenStatsPanel", () => {
|
|
it("renders aggregate totals from agent cumulative token fields", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Alpha", totalInputTokens: 100, totalOutputTokens: 40 }),
|
|
makeAgent({ id: "a-2", name: "Beta", totalInputTokens: 10, totalOutputTokens: 5 }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("Input Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("110")).toBeInTheDocument();
|
|
expect(screen.getByText("Output Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("45")).toBeInTheDocument();
|
|
expect(screen.getByText("Combined Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("155")).toBeInTheDocument();
|
|
});
|
|
|
|
it("sorts per-agent rows by total usage descending", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Alpha", totalInputTokens: 50, totalOutputTokens: 20 }),
|
|
makeAgent({ id: "a-2", name: "Beta", totalInputTokens: 5, totalOutputTokens: 5 }),
|
|
makeAgent({ id: "a-3", name: "Gamma", totalInputTokens: 20, totalOutputTokens: 40 }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
const rows = screen.getAllByRole("row").slice(1);
|
|
expect(within(rows[0]).getByText("Alpha")).toBeInTheDocument();
|
|
expect(within(rows[1]).getByText("Gamma")).toBeInTheDocument();
|
|
expect(within(rows[2]).getByText("Beta")).toBeInTheDocument();
|
|
});
|
|
|
|
it("treats missing token fields as zero and shows empty state when there is no usage", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Zero One", totalInputTokens: undefined, totalOutputTokens: undefined }),
|
|
makeAgent({ id: "a-2", name: "Zero Two" }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("No token usage recorded yet. Token totals appear here once agents run.")).toBeInTheDocument();
|
|
expect(screen.queryByRole("table")).toBeNull();
|
|
expect(screen.getAllByText("0").length).toBeGreaterThanOrEqual(3);
|
|
});
|
|
});
|