diff --git a/apps/web/app/github/page.test.tsx b/apps/web/app/github/page.test.tsx new file mode 100644 index 0000000000..79b14cd250 --- /dev/null +++ b/apps/web/app/github/page.test.tsx @@ -0,0 +1,76 @@ +import { describe, expect, it, vi, beforeEach } from "vitest"; +import { render } from "@testing-library/react"; + +const mocks = vi.hoisted(() => ({ + listWorkspacesAction: vi.fn(), + listWorkflowsAction: vi.fn(), + listRepositoriesAction: vi.fn(), + listWorkspaceWorkflowStepsAction: vi.fn(), + fetchUserSettings: vi.fn(), + cookies: vi.fn(), + capturedInitialState: null as unknown, +})); + +vi.mock("@/app/actions/workspaces", () => ({ + listWorkspacesAction: mocks.listWorkspacesAction, + listWorkflowsAction: mocks.listWorkflowsAction, + listRepositoriesAction: mocks.listRepositoriesAction, + listWorkspaceWorkflowStepsAction: mocks.listWorkspaceWorkflowStepsAction, +})); + +vi.mock("@/lib/api", () => ({ + fetchUserSettings: mocks.fetchUserSettings, +})); + +vi.mock("next/headers", () => ({ + cookies: mocks.cookies, +})); + +vi.mock("@/components/state-hydrator", () => ({ + StateHydrator: ({ initialState }: { initialState: unknown }) => { + mocks.capturedInitialState = initialState; + return null; + }, +})); + +vi.mock("./github-page-client", () => ({ + GitHubPageClient: () => null, +})); + +import GitHubPage from "./page"; + +describe("GitHubPage", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.capturedInitialState = null; + mocks.listWorkspacesAction.mockResolvedValue({ + workspaces: [ + { id: "ws-go", name: "GO" }, + { id: "ws-connection", name: "Connection" }, + ], + }); + mocks.fetchUserSettings.mockResolvedValue({ + settings: { workspace_id: "ws-connection" }, + }); + mocks.cookies.mockResolvedValue({ + get: (name: string) => + name === "office-active-workspace" ? { value: "ws-go" } : undefined, + }); + mocks.listWorkflowsAction.mockResolvedValue({ workflows: [] }); + mocks.listRepositoriesAction.mockResolvedValue({ repositories: [] }); + mocks.listWorkspaceWorkflowStepsAction.mockResolvedValue({ steps: [] }); + }); + + it("prefers the active workspace cookie over stale saved user settings", async () => { + const ui = await GitHubPage({}); + render(ui); + + expect(mocks.listWorkflowsAction).toHaveBeenCalledWith("ws-go"); + expect(mocks.listRepositoriesAction).toHaveBeenCalledWith("ws-go"); + expect(mocks.listWorkspaceWorkflowStepsAction).toHaveBeenCalledWith("ws-go"); + expect(mocks.capturedInitialState).toMatchObject({ + workspaces: { activeId: "ws-go" }, + userSettings: { workspaceId: "ws-go" }, + }); + }); +}); diff --git a/apps/web/app/github/page.tsx b/apps/web/app/github/page.tsx index c0630c1e66..9c3c477773 100644 --- a/apps/web/app/github/page.tsx +++ b/apps/web/app/github/page.tsx @@ -1,3 +1,4 @@ +import { cookies } from "next/headers"; import { listWorkspacesAction, listWorkflowsAction, @@ -7,6 +8,7 @@ import { import { fetchUserSettings } from "@/lib/api"; import { StateHydrator } from "@/components/state-hydrator"; import { mapUserSettingsResponse } from "@/lib/ssr/user-settings"; +import { resolveActiveId } from "@/lib/ssr/resolve-active-id"; import { GitHubPageClient } from "./github-page-client"; import type { Workflow, @@ -17,7 +19,15 @@ import type { } from "@/lib/types/http"; import type { AppState } from "@/lib/state/store"; -export default async function GitHubPage() { +type PageProps = { + searchParams?: Promise>; +}; + +function resolveParam(value: string | string[] | undefined): string | undefined { + return Array.isArray(value) ? value[0] : value; +} + +export default async function GitHubPage({ searchParams }: PageProps = {}) { let workspaces: Workspace[] = []; let workflows: Workflow[] = []; let steps: WorkflowStep[] = []; @@ -27,13 +37,23 @@ export default async function GitHubPage() { let userSettingsResponse: UserSettingsResponse | null = null; try { - const [workspacesResponse, settingsResponse] = await Promise.all([ + const resolvedParams = searchParams ? await searchParams : {}; + const workspaceIdParam = resolveParam(resolvedParams.workspaceId); + const [workspacesResponse, settingsResponse, cookieStore] = await Promise.all([ listWorkspacesAction(), fetchUserSettings({ cache: "no-store" }).catch(() => null), + cookies().catch((error) => { + console.error("Failed to read cookies on GitHub page:", error); + return null; + }), ]); workspaces = workspacesResponse.workspaces; userSettingsResponse = settingsResponse; - workspaceId = settingsResponse?.settings?.workspace_id || workspaces[0]?.id; + const settingsWorkspaceId = settingsResponse?.settings?.workspace_id || null; + const cookieWorkspaceId = cookieStore?.get("office-active-workspace")?.value ?? null; + workspaceId = + resolveActiveId(workspaces, workspaceIdParam, cookieWorkspaceId, settingsWorkspaceId) ?? + undefined; if (workspaceId) { const [workflowsRes, reposRes, stepsRes] = await Promise.all([