dd9b23106c
- 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.
77 lines
2.8 KiB
TypeScript
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");
|
|
});
|
|
});
|