fix(workspace): a new tab shows in its workspace; a row opens its tabs
A tab opened bare from + had no session row until its first hook, and the sidebar re-read its live panes only when a workspace's status or session moved, which a tab in slots 1-3 never does: the new tab showed nowhere. The sidebar now lists a live AI tab whose session has no row yet as "New session" straight from the run (its session id is known from the start) and follows the Workspace page's own run-list poll (LIVE_RUNS_EVENT). A sidebar workspace row now opens the workspace's live tabs on the pane it last focused, and its home only when it has none; a Home button at the left end of the tab bar reaches the home of a workspace whose tabs keep running.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, waitFor, within, fireEvent } from "@testing-library/react";
|
||||
import { act, render, screen, waitFor, within, fireEvent } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
|
||||
import type { Lane, Session } from "../../lib/types";
|
||||
@@ -243,13 +243,93 @@ describe("WorkspaceTree", () => {
|
||||
expect(api.lanes.action).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows a live tab whose session has no row yet as a New session, never a shell", async () => {
|
||||
vi.mocked(api.run.list).mockImplementation(
|
||||
async () =>
|
||||
({
|
||||
items: [
|
||||
{
|
||||
id: "agenthub-lane-1-2",
|
||||
laneId: 1,
|
||||
status: "running",
|
||||
sessionId: "s-live",
|
||||
tool: "claude",
|
||||
},
|
||||
{
|
||||
id: "agenthub-lane-1",
|
||||
laneId: 1,
|
||||
status: "running",
|
||||
sessionId: "s-fresh",
|
||||
tool: "claude",
|
||||
},
|
||||
{
|
||||
id: "agenthub-lane-1-3",
|
||||
laneId: 1,
|
||||
status: "running",
|
||||
sessionId: null,
|
||||
tool: "shell",
|
||||
},
|
||||
],
|
||||
}) as never
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
renderTree();
|
||||
await user.click(await screen.findByTestId("workspace-toggle-1"));
|
||||
const fresh = await screen.findByTestId("workspace-session-new-agenthub-lane-1");
|
||||
expect(fresh).toHaveTextContent("New session");
|
||||
expect(screen.queryByTestId("workspace-session-new-agenthub-lane-1-3")).toBeNull();
|
||||
expect(screen.queryByTestId("workspace-session-new-agenthub-lane-1-2")).toBeNull();
|
||||
await user.click(fresh);
|
||||
expect(screen.getByTestId("where").textContent).toBe("/workspace?lane=1&run=agenthub-lane-1");
|
||||
});
|
||||
|
||||
it("selects by URL, and a click selects and expands", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderTree("/workspace?lane=2");
|
||||
expect(await screen.findByTestId("workspace-open-2")).toHaveAttribute("aria-current", "true");
|
||||
// Lane 1 has a live tab: its row opens the tab, not the home.
|
||||
await waitFor(() => expect(api.run.list).toHaveBeenCalled());
|
||||
await user.click(screen.getByTestId("workspace-open-1"));
|
||||
expect(screen.getByTestId("where").textContent).toBe("/workspace?lane=1");
|
||||
expect(screen.getByTestId("where").textContent).toBe("/workspace?lane=1&run=agenthub-lane-1-2");
|
||||
expect(await screen.findByTestId("workspace-sessions-1")).toBeInTheDocument();
|
||||
// Lane 2 has none: its home.
|
||||
await user.click(screen.getByTestId("workspace-open-2"));
|
||||
expect(screen.getByTestId("where").textContent).toBe("/workspace?lane=2");
|
||||
});
|
||||
|
||||
it("opens a workspace on the pane it last focused, and follows the page's run list", async () => {
|
||||
vi.mocked(api.run.list).mockImplementation(async () => ({ items: [] }) as never);
|
||||
localStorage.setItem(
|
||||
"agenthub.workspace.paneLayout",
|
||||
JSON.stringify({
|
||||
1: {
|
||||
root: {
|
||||
type: "split",
|
||||
dir: "row",
|
||||
ratio: 0.5,
|
||||
a: { type: "pane", slot: 0 },
|
||||
b: { type: "pane", slot: 2 },
|
||||
},
|
||||
focus: 2,
|
||||
},
|
||||
})
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
renderTree();
|
||||
await screen.findByTestId("workspace-open-1");
|
||||
// The Workspace page's poll lands: two live tabs on lane 1.
|
||||
act(() => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("agenthub:live-runs", {
|
||||
detail: [
|
||||
{ id: "agenthub-lane-1", laneId: 1, slot: 0, status: "running", tool: "claude" },
|
||||
{ id: "agenthub-lane-1-2", laneId: 1, slot: 2, status: "running", tool: "claude" },
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
await user.click(screen.getByTestId("workspace-open-1"));
|
||||
expect(screen.getByTestId("where").textContent).toBe("/workspace?lane=1&run=agenthub-lane-1-2");
|
||||
});
|
||||
|
||||
it("reorders with Alt+ArrowUp and keeps the order in localStorage", async () => {
|
||||
|
||||
Reference in New Issue
Block a user