Skip to content

Commit 426f52d

Browse files
test(frontend): verify PR hydration end-to-end for a normal project (#251)
Drives the real useWorkspaceQuery + real SessionsBoard / PullRequestsPage for an ordinary project (from /api/v1/projects) whose session has an open PR, mocking only the HTTP client and router. Confirms PR facts fetched from /sessions/{id}/pr flow through the shared workspace cache into both the Board card ("PR #278 · open") and the PR page row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a238ec0 commit 426f52d

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2+
import { render, screen } from "@testing-library/react";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
4+
import type { ReactNode } from "react";
5+
6+
// Drives the real useWorkspaceQuery + real Board / PR-page consumers end to end
7+
// for a normal project, mocking only the HTTP client and the router. Proves the
8+
// #251 fix: PR facts fetched from /pr flow through the shared workspace cache
9+
// into every consumer.
10+
const { getMock, navigateMock } = vi.hoisted(() => ({ getMock: vi.fn(), navigateMock: vi.fn() }));
11+
12+
vi.mock("../lib/api-client", () => ({
13+
apiClient: { GET: getMock, POST: vi.fn() },
14+
apiErrorMessage: (e: unknown) => (e instanceof Error ? e.message : "error"),
15+
}));
16+
17+
vi.mock("@tanstack/react-router", async (importOriginal) => {
18+
const actual = await importOriginal<typeof import("@tanstack/react-router")>();
19+
return { ...actual, useNavigate: () => navigateMock };
20+
});
21+
22+
import { SessionsBoard } from "./SessionsBoard";
23+
import { PullRequestsPage } from "./PullRequestsPage";
24+
25+
// One ordinary project with one worker session that has an open PR (#278).
26+
function respondWithProjectAndPR() {
27+
getMock.mockImplementation(async (url: string, options?: { params?: { path?: { sessionId?: string } } }) => {
28+
if (url === "/api/v1/projects") {
29+
return { data: { projects: [{ id: "proj-1", name: "my-app", path: "/repo/my-app" }] }, error: undefined };
30+
}
31+
if (url === "/api/v1/sessions") {
32+
return {
33+
data: {
34+
sessions: [
35+
{
36+
id: "sess-1",
37+
projectId: "proj-1",
38+
displayName: "fix the bug",
39+
harness: "claude-code",
40+
status: "pr_open",
41+
isTerminated: false,
42+
updatedAt: "2026-06-10T16:15:04Z",
43+
},
44+
],
45+
},
46+
error: undefined,
47+
};
48+
}
49+
if (url === "/api/v1/sessions/{sessionId}/pr") {
50+
expect(options?.params?.path?.sessionId).toBe("sess-1");
51+
return {
52+
data: {
53+
sessionId: "sess-1",
54+
prs: [
55+
{
56+
number: 278,
57+
state: "open",
58+
url: "https://github.com/aoagents/ReverbCode/pull/278",
59+
ci: "passing",
60+
review: "approved",
61+
mergeability: "clean",
62+
reviewComments: false,
63+
updatedAt: "2026-06-10T16:15:04Z",
64+
},
65+
],
66+
},
67+
error: undefined,
68+
};
69+
}
70+
throw new Error(`unexpected GET ${url}`);
71+
});
72+
}
73+
74+
function renderWithProviders(node: ReactNode) {
75+
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
76+
render(<QueryClientProvider client={queryClient}>{node}</QueryClientProvider>);
77+
}
78+
79+
beforeEach(() => {
80+
getMock.mockReset();
81+
navigateMock.mockReset();
82+
respondWithProjectAndPR();
83+
});
84+
85+
describe("PR hydration for a normal project (#251)", () => {
86+
it("renders the PR on the Board card instead of 'no PR yet'", async () => {
87+
renderWithProviders(<SessionsBoard />);
88+
89+
expect(await screen.findByText("PR #278 · open")).toBeInTheDocument();
90+
expect(screen.queryByText("no PR yet")).not.toBeInTheDocument();
91+
});
92+
93+
it("lists the session on the PR page instead of being empty", async () => {
94+
renderWithProviders(<PullRequestsPage />);
95+
96+
expect(await screen.findByText("#278")).toBeInTheDocument();
97+
expect(screen.queryByText("No open pull requests.")).not.toBeInTheDocument();
98+
expect(screen.getByText("fix the bug")).toBeInTheDocument();
99+
});
100+
});

0 commit comments

Comments
 (0)