Files
AgentHub/client/src/components/run/__tests__/ConsoleTabs.presence.test.tsx
T
nntrivi2001 18e574a765 feat(server-mode): pane presence and typing audit (sharing phase 4)
A pane's tab shows the other people who have it open (pane-presence.js,
in memory, fed by the /ws-pty attach; GET /api/lanes/:id/presence and the
pane_presence broadcast to the owner and full collaborators) and says
"Billed to <name>" for a pane a collaborator opened (startedBy on the run).
The first real keystroke of each attach into a shared workspace's pane by
anyone but its owner is audited as `type into <run id>` (mouse, focus and
terminal replies to tmux do not count). The pty socket now pings, so a
vanished browser is ended at the next re-check and leaves the presence list.
2026-11-06 11:32:25 +07:00

80 lines
2.4 KiB
TypeScript

/**
* @file ConsoleTabs.presence.test.tsx
* @description Workspace sharing phase 4 on the tab bar: a tab shows who ELSE
* has its panes open (never the viewer), and its tooltip says whose money a
* pane a collaborator opened is.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
import { describe, expect, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import "../../../i18n";
import type { RunHandle } from "../../../lib/api";
import type { Lane } from "../../../lib/types";
import { pane, type PaneGroups } from "../../../lib/paneLayout";
import { ConsoleTabs } from "../ConsoleTabs";
vi.mock("../../../lib/api", () => ({
api: {
aiTools: {
list: vi.fn().mockResolvedValue({ default: "claude", tools: [] }),
models: vi.fn().mockResolvedValue({ flag: true, models: [], defaultModel: null }),
},
},
}));
vi.mock("../../AuthGate", () => ({
useAuth: () => ({ state: { mode: "server", user: { id: 1, username: "alice" } } }),
usePermissions: () => ({ local: false, isAdmin: false }),
}));
vi.mock("../../../hooks/usePanePresence", () => ({
usePanePresence: () => ({
"agenthub-lane-1": [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
],
}),
}));
const LANE = { id: 1, tool: "claude", cwd: "/w", title: "w" } as unknown as Lane;
const RUN: RunHandle = {
id: "agenthub-lane-1",
laneId: 1,
slot: 0,
status: "running",
cwd: "/w",
model: null,
permissionMode: null,
effort: null,
resumeSessionId: null,
sessionId: null,
startedAt: null,
promptPreview: null,
startedBy: { id: 2, name: "Bob" },
tool: "claude",
};
const GROUPS: PaneGroups = { groups: [{ root: pane(0), focus: 0 }], active: 0 };
describe("ConsoleTabs presence", () => {
it("shows who else is in a pane, and whose money it is", () => {
render(
<MemoryRouter>
<ConsoleTabs
lane={LANE}
tabs={[RUN]}
groups={GROUPS}
onGroupsChange={vi.fn()}
onOpen={vi.fn()}
onClosed={vi.fn()}
/>
</MemoryRouter>
);
const people = screen.getAllByTestId(/^console-tab-people-/)[0]!;
expect(people.textContent).toBe("B");
expect(people.getAttribute("aria-label")).toBe("Also here: Bob");
const tab = screen.getAllByRole("tab")[0]!;
expect(tab.getAttribute("title")).toContain("Billed to Bob");
expect(tab.getAttribute("title")).not.toContain("Alice");
});
});