Files
AgentHub/client/src/pages/__tests__/AgentsTable.test.tsx
T
nntrivi2001 dd9b23106c feat: v2.5.4 — Query Explorer folded into Sessions, Windows picker and paths
- Sessions page tabs: Sessions · Events · Agents. QueryExplorer.tsx became
  AgentsTable.tsx (agents only — sessions and events already had tabs);
  /query redirects to /sessions?view=agents and leaves the sidebar.
  GET /api/query is unchanged and still answers all three entities.
- Windows picker opens in front: -EncodedCommand script spends the hidden
  first ShowWindow that windowsHide gives the process, then brings the TopMost
  owner forward via AttachThreadInput (v2.5.3's owner was itself hidden).
- Session page Resume/Fork/Open pane: owningLane and sessionWorktreeName
  match Windows folders (either separator), with a test.
2026-10-06 21:56:51 +07:00

77 lines
2.8 KiB
TypeScript

/**
* @file AgentsTable.test.tsx
* @description The Sessions page's Agents tab: it asks only for agents, renders
* a page of rows with session links, sorts by an allowed column on a header
* click (flipping on a second), and points both export links at the filter.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import i18n from "i18next";
const { query } = vi.hoisted(() => ({
query: {
page: vi.fn(),
exportUrl: (p: URLSearchParams, f: string) => `/api/query?${p.toString()}&format=${f}`,
},
}));
vi.mock("../../lib/api", () => ({ api: { query } }));
vi.mock("../../hooks/useAiTools", () => ({
useAiTools: () => ({ tools: [{ id: "codex", label: "Codex" }] }),
}));
import { AgentsTable } from "../AgentsTable";
const lastParams = () => {
const calls = query.page.mock.calls;
return calls[calls.length - 1]![0] as URLSearchParams;
};
beforeEach(() => {
i18n.changeLanguage("en");
vi.clearAllMocks();
query.page.mockResolvedValue({
entity: "agents",
columns: ["id", "session_id", "name", "started_at"],
sortable: ["started_at", "name"],
statuses: ["working", "waiting", "completed", "error"],
rows: [{ id: "a1", session_id: "sess-abcdef12", name: "first", started_at: "2026-10-01" }],
total: 1,
limit: 50,
offset: 0,
export_cap: 50000,
});
});
describe("AgentsTable", () => {
it("lists agents, sorts by an allowed column and exports the filter", async () => {
const user = userEvent.setup();
render(
<MemoryRouter>
<AgentsTable />
</MemoryRouter>
);
expect(await screen.findByText("first")).toBeInTheDocument();
expect(lastParams().get("entity")).toBe("agents");
expect(screen.getByRole("link", { name: "sess-abc" })).toHaveAttribute(
"href",
"/sessions/sess-abcdef12"
);
expect(screen.getByRole("button", { name: /Status/ })).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "name" }));
await waitFor(() => expect(lastParams().get("sort")).toBe("name"));
expect(lastParams().get("dir")).toBe("desc");
await user.click(screen.getByRole("button", { name: "name" }));
await waitFor(() => expect(lastParams().get("dir")).toBe("asc"));
// A column outside `sortable` has no sort button.
expect(screen.queryByRole("button", { name: "id" })).toBeNull();
const csv = screen.getByRole("link", { name: /Export CSV/ });
expect(csv.getAttribute("href")).toContain("entity=agents");
expect(csv.getAttribute("href")).toContain("sort=name");
expect(csv.getAttribute("href")).toContain("format=csv");
});
});