diff options
| author | Adam Malczewski <[email protected]> | 2026-06-25 17:09:57 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-25 17:09:57 +0900 |
| commit | 6b0bb19e914a3bbaeca198ac1627d698c3a13a76 (patch) | |
| tree | 5f9a9c0856e0eefa908963d6e0d650b2853808a5 /src/features/workspaces/ui/WorkspaceCard.test.ts | |
| parent | 38db3827870960f466be89afbc49f91238d46144 (diff) | |
| download | dispatch-web-6b0bb19e914a3bbaeca198ac1627d698c3a13a76.tar.gz dispatch-web-6b0bb19e914a3bbaeca198ac1627d698c3a13a76.zip | |
feat(computer): SSH computer selection + status + workspace default (handoff #2)
Mirrors the cwd/workspace UI for the SSH-computer feature:
- New feature library src/features/computer/:
- logic/view-model.ts (pure): viewComputer/viewComputerStatus/viewTestResult/
summarizeComputers/formatHost/knownHostLabel + state->badge for the 4
ComputerStatusResponse states + SaveComputer/LoadComputerStatus/
TestComputer/LoadComputers ports. 20 view-model tests.
- ui/ComputerField.svelte: per-conversation selector (dropdown +
connection-status badge + Test-connection, polling the selected alias).
- ui/ComputerSelect.svelte: reusable Local/computers dropdown, shared with
the workspace default-computer control.
- AppStore: computerId state + refreshComputer (at every focus site, parallel
to refreshCwd) + setComputer (PUT /conversations/:id/computer, null=clear) +
global computers catalog (GET /computers on boot, like models) +
computerStatus(alias) + testComputer(alias). chat.send UNCHANGED (resolved
server-side like cwd).
- App.svelte: ComputerField in the Model sidebar view next to CwdField; adapted
ports wrap the store.
- workspaces: setDefaultComputer on WorkspaceHttp+WorkspaceStore
(PUT /workspaces/:id/default-computer); default-computer selector in
WorkspaceCard (reuses ComputerSelect); router passes store.computers through.
- Re-mirrored .dispatch/transport-contract.reference.md (Computers section +
ChatRequest.computerId); updated .dispatch/wire.reference.md (Computer/
ComputerEntry/defaultComputerId + the provider-retry divergence note from
handoff #1); GLOSSARY + backend-handoff.md (handoff #2, §2e).
Transparency invariant: the computer is USER-facing only (a tool-execution
target, never part of the model prompt); the agent never sees it.
Verify: 795/795 tests green; biome clean; vite build succeeds; 0 typecheck
errors from the computer feature. (11 pre-existing svelte-check errors remain
from the open §2d provider-retry divergence — backend feature/ssh-support
still lacks TurnProviderRetryEvent; not from this feature.)
Diffstat (limited to 'src/features/workspaces/ui/WorkspaceCard.test.ts')
| -rw-r--r-- | src/features/workspaces/ui/WorkspaceCard.test.ts | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/src/features/workspaces/ui/WorkspaceCard.test.ts b/src/features/workspaces/ui/WorkspaceCard.test.ts index 385856d..48b12b3 100644 --- a/src/features/workspaces/ui/WorkspaceCard.test.ts +++ b/src/features/workspaces/ui/WorkspaceCard.test.ts @@ -11,6 +11,7 @@ function fakeEntry(overrides: Partial<WorkspaceEntry> = {}): WorkspaceEntry { id: "my-ws", title: "My Workspace", defaultCwd: null, + defaultComputerId: null, createdAt: 1, lastActivityAt: 2, conversationCount: 3, @@ -33,6 +34,12 @@ function fakeStore() { value: fakeEntry({ id, defaultCwd }), }), ), + setDefaultComputer: vi.fn( + async (id: string, computerId: string | null): Promise<WorkspaceResult<WorkspaceEntry>> => ({ + ok: true, + value: fakeEntry({ id, defaultComputerId: computerId }), + }), + ), remove: vi.fn( async (): Promise<WorkspaceResult<{ closedCount: number }>> => ({ ok: true, @@ -47,7 +54,7 @@ describe("WorkspaceCard", () => { const store = fakeStore() as unknown as WorkspaceStore; const onNavigate = vi.fn(); render(WorkspaceCard, { - props: { ws: fakeEntry(), store, onNavigate }, + props: { ws: fakeEntry(), store, onNavigate, computers: [] }, }); expect(screen.getByText("My Workspace")).toBeInTheDocument(); expect(screen.getByText("/my-ws")).toBeInTheDocument(); @@ -57,7 +64,9 @@ describe("WorkspaceCard", () => { it("double-clicking the title reveals an edit input", async () => { const user = userEvent.setup(); const store = fakeStore() as unknown as WorkspaceStore; - render(WorkspaceCard, { props: { ws: fakeEntry(), store, onNavigate: vi.fn() } }); + render(WorkspaceCard, { + props: { ws: fakeEntry(), store, onNavigate: vi.fn(), computers: [] }, + }); await user.dblClick(screen.getByText("My Workspace")); expect(screen.getByLabelText("Workspace title")).toHaveValue("My Workspace"); @@ -66,7 +75,9 @@ describe("WorkspaceCard", () => { it("renames via the store on Enter", async () => { const user = userEvent.setup(); const store = fakeStore() as unknown as WorkspaceStore; - render(WorkspaceCard, { props: { ws: fakeEntry(), store, onNavigate: vi.fn() } }); + render(WorkspaceCard, { + props: { ws: fakeEntry(), store, onNavigate: vi.fn(), computers: [] }, + }); await user.dblClick(screen.getByText("My Workspace")); const input = screen.getByLabelText("Workspace title"); @@ -80,7 +91,7 @@ describe("WorkspaceCard", () => { const user = userEvent.setup(); const store = fakeStore() as unknown as WorkspaceStore; render(WorkspaceCard, { - props: { ws: fakeEntry({ defaultCwd: "/old" }), store, onNavigate: vi.fn() }, + props: { ws: fakeEntry({ defaultCwd: "/old" }), store, onNavigate: vi.fn(), computers: [] }, }); const input = screen.getByLabelText("Default working directory"); @@ -99,7 +110,7 @@ describe("WorkspaceCard", () => { const user = userEvent.setup(); const store = fakeStore() as unknown as WorkspaceStore; render(WorkspaceCard, { - props: { ws: fakeEntry({ defaultCwd: "/old" }), store, onNavigate: vi.fn() }, + props: { ws: fakeEntry({ defaultCwd: "/old" }), store, onNavigate: vi.fn(), computers: [] }, }); const input = screen.getByLabelText("Default working directory"); @@ -113,7 +124,7 @@ describe("WorkspaceCard", () => { const user = userEvent.setup(); const store = fakeStore() as unknown as WorkspaceStore; const onNavigate = vi.fn(); - render(WorkspaceCard, { props: { ws: fakeEntry(), store, onNavigate } }); + render(WorkspaceCard, { props: { ws: fakeEntry(), store, onNavigate, computers: [] } }); await user.click(screen.getByRole("link", { name: "Open" })); expect(onNavigate).toHaveBeenCalledWith("/my-ws"); |
