Skip to content

Commit da939aa

Browse files
fix(site): move pagination test from vitest to storybook story (cherry-pick #24165) (#25238)
Cherry-pick of #24165 to `release/2.32`. Moves the flaky pagination query key test from vitest to a Storybook story. The test timed out in CI because `renderWithAuth` boots 12+ MSW round-trips before the page mounts. The story uses decorators to pre-seed the query cache, skipping the MSW waterfall entirely. > 🤖 Generated by Coder Agent Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
1 parent bbe0286 commit da939aa

3 files changed

Lines changed: 50 additions & 65 deletions

File tree

site/src/api/queries/workspaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ async function findMatchWorkspace(q: string): Promise<Workspace | undefined> {
211211
}
212212
}
213213

214-
function workspacesKey(req: WorkspacesRequest = {}) {
214+
export function workspacesKey(req: WorkspacesRequest = {}) {
215215
return ["workspaces", req] as const;
216216
}
217217

site/src/pages/WorkspacesPage/WorkspacesPage.stories.tsx

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getTemplatesQueryKey,
1515
templateVersionsQueryKey,
1616
} from "#/api/queries/templates";
17+
import { workspacesKey } from "#/api/queries/workspaces";
1718
import type { Workspace } from "#/api/typesGenerated";
1819
import { workspaceChecks } from "#/modules/workspaces/permissions";
1920
import {
@@ -22,6 +23,7 @@ import {
2223
MockTemplate,
2324
MockTemplateVersion,
2425
MockUserOwner,
26+
MockWorkspace,
2527
} from "#/testHelpers/entities";
2628
import {
2729
withAuthProvider,
@@ -55,7 +57,7 @@ const deletingWorkspace: Workspace = {
5557
},
5658
};
5759

58-
const meta: Meta<typeof WorkspacesPage> = {
60+
const meta = {
5961
title: "pages/WorkspacesPage/WorkspacesPage",
6062
component: WorkspacesPage,
6163
decorators: [withAuthProvider, withDashboardProvider, withProxyProvider()],
@@ -108,10 +110,10 @@ const meta: Meta<typeof WorkspacesPage> = {
108110
spyOn(API, "getOrganizations").mockResolvedValue([MockDefaultOrganization]);
109111
spyOn(API, "getWorkspaceBuildParameters").mockResolvedValue([]);
110112
},
111-
};
113+
} satisfies Meta<typeof WorkspacesPage>;
112114

113115
export default meta;
114-
type Story = StoryObj<typeof WorkspacesPage>;
116+
type Story = StoryObj<typeof meta>;
115117

116118
export const DeleteWorkspaceShowsDeletingStateImmediately: Story = {
117119
beforeEach: () => {
@@ -170,3 +172,47 @@ export const DeleteWorkspaceShowsDeletingStateImmediately: Story = {
170172
);
171173
},
172174
};
175+
176+
const makePage = (prefix: string) =>
177+
Array.from({ length: 25 }, (_, i) => ({
178+
...MockWorkspace,
179+
id: `${prefix}-workspace-${i}`,
180+
name: `${prefix}-workspace-${i}`,
181+
}));
182+
183+
export const PaginationChangesQueryKey: Story = {
184+
parameters: {
185+
chromatic: { disableSnapshot: true },
186+
queries: [
187+
...meta.parameters.queries,
188+
{
189+
key: workspacesKey({ q: "owner:me", limit: 25, offset: 0 }),
190+
data: { workspaces: makePage("page1"), count: 50 },
191+
},
192+
{
193+
key: workspacesKey({ q: "owner:me", limit: 25, offset: 25 }),
194+
data: { workspaces: makePage("page2"), count: 50 },
195+
},
196+
],
197+
},
198+
play: async ({ canvasElement, step }) => {
199+
const canvas = within(canvasElement);
200+
const user = userEvent.setup();
201+
202+
await step("Page 1 renders from cache", async () => {
203+
await canvas.findByText("page1-workspace-0");
204+
});
205+
206+
await step("Clicking next page shows page 2 data", async () => {
207+
const nextButton = await canvas.findByRole("button", {
208+
name: /next page/i,
209+
});
210+
await user.click(nextButton);
211+
212+
await canvas.findByText("page2-workspace-0");
213+
await waitFor(() => {
214+
expect(canvas.queryByText("page1-workspace-0")).not.toBeInTheDocument();
215+
});
216+
});
217+
},
218+
};

site/src/pages/WorkspacesPage/WorkspacesPage.test.tsx

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -296,67 +296,6 @@ describe("WorkspacesPage", () => {
296296
MockStoppedWorkspace.latest_build.template_version_id,
297297
);
298298
});
299-
300-
it("correctly handles pagination by including pagination parameters in query key", async () => {
301-
const totalWorkspaces = 50;
302-
const workspacesPage1 = Array.from({ length: 25 }, (_, i) => ({
303-
...MockWorkspace,
304-
id: `page1-workspace-${i}`,
305-
name: `page1-workspace-${i}`,
306-
}));
307-
const workspacesPage2 = Array.from({ length: 25 }, (_, i) => ({
308-
...MockWorkspace,
309-
id: `page2-workspace-${i}`,
310-
name: `page2-workspace-${i}`,
311-
}));
312-
313-
const getWorkspacesSpy = vi.spyOn(API, "getWorkspaces");
314-
315-
getWorkspacesSpy.mockImplementation(({ offset }) => {
316-
switch (offset) {
317-
case 0:
318-
return Promise.resolve({
319-
workspaces: workspacesPage1,
320-
count: totalWorkspaces,
321-
});
322-
case 25:
323-
return Promise.resolve({
324-
workspaces: workspacesPage2,
325-
count: totalWorkspaces,
326-
});
327-
default:
328-
return Promise.reject(new Error("Unexpected offset"));
329-
}
330-
});
331-
332-
const user = userEvent.setup();
333-
renderWithAuth(<WorkspacesPage />);
334-
335-
await waitFor(() => {
336-
expect(screen.getByText("page1-workspace-0")).toBeInTheDocument();
337-
});
338-
339-
expect(getWorkspacesSpy).toHaveBeenLastCalledWith({
340-
q: "owner:me",
341-
offset: 0,
342-
limit: 25,
343-
});
344-
345-
const nextPageButton = screen.getByRole("button", { name: /next page/i });
346-
await user.click(nextPageButton);
347-
348-
await waitFor(() => {
349-
expect(screen.getByText("page2-workspace-0")).toBeInTheDocument();
350-
});
351-
352-
expect(getWorkspacesSpy).toHaveBeenLastCalledWith({
353-
q: "owner:me",
354-
offset: 25,
355-
limit: 25,
356-
});
357-
358-
expect(screen.queryByText("page1-workspace-0")).not.toBeInTheDocument();
359-
});
360299
});
361300

362301
const getWorkspaceCheckbox = (workspace: Workspace) => {

0 commit comments

Comments
 (0)