Skip to content

fix: fix workspaces pagination #19448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 21, 2025
Merged

fix: fix workspaces pagination #19448

merged 5 commits into from
Aug 21, 2025

Conversation

BrunoQuaresma
Copy link
Collaborator

Fixes #18707

Before:

Screen.Recording.2025-08-20.at.10.44.36.mov

After:

Screen.Recording.2025-08-20.at.10.44.52.mov

@BrunoQuaresma BrunoQuaresma requested review from Parkreiner and a team August 20, 2025 13:47
@BrunoQuaresma BrunoQuaresma self-assigned this Aug 20, 2025
@BrunoQuaresma BrunoQuaresma requested a review from aslilac as a code owner August 20, 2025 13:47
Copy link
Member

@aslilac aslilac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the actual guts of the change are straight forward and good, but the test seems a bit wonky. can we pls clean it up a little more before merging?

const secondCallArgs = getWorkspacesSpy.mock.calls[1][0];
expect(firstCallArgs.offset).toBe(0);
expect(secondCallArgs.offset).toBe(25);
expect(firstCallArgs.offset).not.toBe(secondCallArgs.offset);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant, we just asserted that it’s 0


// Verify the key difference: the offset changed between calls
const firstCallArgs = getWorkspacesSpy.mock.calls[0][0];
const secondCallArgs = getWorkspacesSpy.mock.calls[1][0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just asserted on this with toHaveBeenNthCalledWith

const nextPageButton = screen.getByRole("button", { name: /next page/i });
await user.click(nextPageButton);

// Wait for second page to load
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment doesn’t add anything imo

const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);

// Wait for first page to load
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. waitFor is already the name of the function being called.

@BrunoQuaresma BrunoQuaresma requested a review from aslilac August 20, 2025 14:42
Comment on lines 324 to 332
getWorkspacesSpy
.mockResolvedValueOnce({
workspaces: workspacesPage1,
count: totalWorkspaces,
})
.mockResolvedValueOnce({
workspaces: workspacesPage2,
count: totalWorkspaces,
});
Copy link
Member

@Parkreiner Parkreiner Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this setup is a little fragile. I've never stacked multiple calls to mockResolvedValueOnce onto the same spy before, but if I'm reading it right, it means that, no matter what we actually call the function with, we'll always get page 1 for the first call, and page 2 for the second call. But then:

  • Any future calls immediately go back to the actual non-mocked version
  • We never check the inputs for the functions to decide what to return out. If we accidentally pass page 3 in, we might get page 1 or we might get page 2
  • If we try to get page 1 multiple times, we can't

I don't know all the details, but I know that MSW has tools to mock out the API response. Maybe that's not needed and we could keep the Jest Spys, but I think I'd rather make it so that we have a mock that stays locked in for the duration of the test, and then checks the page input:

  • A page value of 1 always gives back page 1 content
  • A page value of 2 always gives back page 2 content
  • Any other page values just give back an empty array

Copy link
Member

@Parkreiner Parkreiner Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you have the checks for the exact inputs a little bit below, but if the React testing tools ever add double-rendering to help check for correctness (like what you get with StrictMode and dev mode), that would immediately cause this test to break

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I got it. Let me share what I understood.

The issue with this test is it is using stacked mockResolvedValueOnce to mock the responses for the first and second calls so, if react renders twice in the same page, the test would break. Is that right?

Copy link
Member

@Parkreiner Parkreiner Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if React ever double-mounts the same hook for the data fetching, this will happen if we start at page 1:

  1. The page component gets mounted, and we trigger the data fetch, using a page value of 1 as input. The function is mocked, so we always get the page 1 data back no matter what
  2. The render completes
  3. React unmounts and re-mounts the component from scratch, which potentially causes a second data fetch, still using a page value of 1 as input
  4. But because we've already popped off the first mock, calling the function with a page value of 1 actually gets us page 2's data, because the mock doesn't actually check what number gets passed in
  5. We get back page 2's data, and render that out. React considers the render "stabilized"
  6. We go back to the testing/assertion logic, and the tests fail because the page 1 content is no longer on screen
  7. If we were to keep going, and try switching the page value to 2 by clicking the UI, we have no more mocks left, and the call goes to the actual API implementation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the spyOn to use the mockImplementation, wdyt?

Copy link
Member

@aslilac aslilac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks bruno! one last suggestion, but this test is looking much better.

Comment on lines 348 to 349
expect(getWorkspacesSpy).toHaveBeenNthCalledWith(
1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(getWorkspacesSpy).toHaveBeenNthCalledWith(
1,
expect(getWorkspacesSpy).toHaveBeenLastCalledWith(

one last thing, I think toHaveBeenLastCalledWith would be better here. to michael's point: how many times this function actually gets called could be considered an implementation detail of react. but we do know we want it to have been called, and that at least the most recent call should match this assertion.

https://jestjs.io/docs/expect#tohavebeenlastcalledwitharg1-arg2-

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! 👍

Comment on lines 364 to 365
expect(getWorkspacesSpy).toHaveBeenNthCalledWith(
2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(getWorkspacesSpy).toHaveBeenNthCalledWith(
2,
expect(getWorkspacesSpy).toHaveBeenLastCalledWith(

same thought here

@BrunoQuaresma BrunoQuaresma merged commit 54440af into main Aug 21, 2025
52 of 56 checks passed
@BrunoQuaresma BrunoQuaresma deleted the bq/18707 branch August 21, 2025 17:59
@github-actions github-actions bot locked and limited conversation to collaborators Aug 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Workspaces list pagination is slow with several hundred workspaces
3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy