From a00fd7e112612cfb97483ae00fcb42548ca3cc72 Mon Sep 17 00:00:00 2001 From: utpal singh Date: Tue, 8 Sep 2026 09:27:19 +0530 Subject: [PATCH] feat: link workspace title to overview --- .../web/src/components/layout/Sidebar.tsx | 13 +- .../src/test/sidebar-workspace-link.test.tsx | 130 ++++++++++++++++++ 2 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 packages/web/src/test/sidebar-workspace-link.test.tsx diff --git a/packages/web/src/components/layout/Sidebar.tsx b/packages/web/src/components/layout/Sidebar.tsx index 97c9d62..73e1c03 100644 --- a/packages/web/src/components/layout/Sidebar.tsx +++ b/packages/web/src/components/layout/Sidebar.tsx @@ -281,15 +281,18 @@ export function Sidebar() { transition={{ duration: 0.22, ease: "easeInOut" }} className="overflow-hidden" > - {/* Workspace ID label */} + {/* Workspace ID — links back to this workspace's overview */}
-

{mask(activeWorkspaceId)} -

+
{/* Section links — indented */} diff --git a/packages/web/src/test/sidebar-workspace-link.test.tsx b/packages/web/src/test/sidebar-workspace-link.test.tsx new file mode 100644 index 0000000..69a6e31 --- /dev/null +++ b/packages/web/src/test/sidebar-workspace-link.test.tsx @@ -0,0 +1,130 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createMemoryHistory, createRouter, RouterProvider } from "@tanstack/react-router"; +import { render, screen, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { DemoProvider } from "@/context/DemoContext"; +import { MetadataProvider } from "@/context/MetadataContext"; +import { saveStore } from "@/lib/config"; +import { routeTree } from "@/routeTree.gen"; + +const { httpFetch } = vi.hoisted(() => ({ httpFetch: vi.fn() })); +vi.mock("@/lib/http", () => ({ httpFetch })); + +const WORKSPACE_ID = "ws-alpha"; +const INSTANCE = { + id: "inst-1", + name: "Local", + baseUrl: "http://localhost:8000", + token: "", +}; + +function jsonResponse(body: unknown = { items: [], total: 0, page: 1, size: 1, pages: 0 }) { + return new Response(JSON.stringify(body), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); +} + +function seedApp() { + saveStore({ instances: [INSTANCE], activeId: INSTANCE.id }); + httpFetch.mockResolvedValue(jsonResponse()); +} + +function renderAt(initialPath: string) { + const router = createRouter({ + routeTree, + history: createMemoryHistory({ initialEntries: [initialPath] }), + }); + const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return { + router, + ...render( + + + + {/* biome-ignore lint/suspicious/noExplicitAny: test router type */} + + + + , + ), + }; +} + +function sidebarWorkspaceLink() { + return within(screen.getByRole("complementary")).getByRole("link", { + name: /workspace overview/i, + }); +} + +describe("sidebar workspace overview link", () => { + afterEach(() => { + httpFetch.mockReset(); + localStorage.clear(); + }); + + it("exposes the workspace label as a link from a nested workspace page", async () => { + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/peers`); + await screen.findByRole("heading", { name: "Peers" }); + expect(sidebarWorkspaceLink()).toHaveAttribute("href", `/workspaces/${WORKSPACE_ID}`); + }); + + it("navigates to the workspace overview when the sidebar label is activated", async () => { + const user = userEvent.setup(); + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/peers`); + await user.click(await screen.findByRole("link", { name: /workspace overview/i })); + expect(await screen.findByText("Workspace overview")).toBeInTheDocument(); + }); + + it("keeps the real workspace id in the link href when demo mode is on", async () => { + localStorage.setItem("openconcho:demo", "true"); + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/peers`); + expect(await screen.findByRole("link", { name: /workspace overview/i })).toHaveAttribute( + "href", + `/workspaces/${WORKSPACE_ID}`, + ); + }); + + it("masks the visible workspace identifier in demo mode", async () => { + localStorage.setItem("openconcho:demo", "true"); + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/peers`); + expect(await screen.findByRole("link", { name: /workspace overview/i })).toHaveTextContent( + "*".repeat(WORKSPACE_ID.length), + ); + }); + + it("puts the masked identifier in the tooltip, not the raw id, in demo mode", async () => { + localStorage.setItem("openconcho:demo", "true"); + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/peers`); + expect(await screen.findByRole("link", { name: /workspace overview/i })).toHaveAttribute( + "title", + "*".repeat(WORKSPACE_ID.length), + ); + }); + + it("does not render a workspace overview link outside workspace routes", async () => { + seedApp(); + renderAt("/"); + await screen.findByRole("link", { name: /dashboard/i }); + expect( + within(screen.getByRole("complementary")).queryByRole("link", { + name: /workspace overview/i, + }), + ).not.toBeInTheDocument(); + }); + + it("leaves the Peers contextual nav link working", async () => { + seedApp(); + renderAt(`/workspaces/${WORKSPACE_ID}/sessions`); + expect(await screen.findByRole("link", { name: /^peers$/i })).toHaveAttribute( + "href", + `/workspaces/${WORKSPACE_ID}/peers`, + ); + }); +});