Skip to content

Conversation

jpggvilaca
Copy link
Contributor

@jpggvilaca jpggvilaca commented Jun 25, 2025

📝 Description

Why did this come up? I wanted to decouple notifications from a few places, which let me to update the mutation cache, which led to tests failing, which led to realising we were using different clients, which might be dangerous since then we'll be testing something different from what our main app uses. And here we are.
QueryClient now triggers notifications instead of each hook doing it. Which means the tests need the correct query client to test the existence of those notifications.

Note: I will wait for the release to merge this.

  • RequiredProvidersRender will now use the actual query client we use for the geti app
  • Extract query client creation to its own function
  • Add mutationCache to our query client to inherit the same behavior we already have with queryCache (to trigger a notification onError)

For the next PRs:

  • Leverage the query and mutation cache and get rid of all addNotification (non-custom ones) from all consumers.

✨ Type of Change

Select the type of change your PR introduces:

  • 🐞 Bug fix – Non-breaking change which fixes an issue
  • 🚀 New feature – Non-breaking change which adds functionality
  • 🔨 Refactor – Non-breaking change which refactors the code base
  • 💥 Breaking change – Changes that break existing functionality
  • 📚 Documentation update
  • 🔒 Security update
  • 🧪 Tests

🧪 Testing Scenarios

Describe how the changes were tested and how reviewers can test them too:

  • ✅ Tested manually
  • 🤖 Run automated end-to-end tests

✅ Checklist

Before submitting the PR, ensure the following:

  • 🔍 PR title is clear and descriptive
  • 📝 For internal contributors: If applicable, include the JIRA ticket number (e.g., ITEP-123456) in the PR title. Do not include full URLs
  • 💬 I have commented my code, especially in hard-to-understand areas
  • 📄 I have made corresponding changes to the documentation
  • ✅ I have added tests that prove my fix is effective or my feature works

const { rerender } = renderHookWithProviders(
({ jobs }) => useInvalidateBalanceOnNewJob(workspaceIdentifier, getMockedResponse(jobs), {}),
({ jobs }) => {
const queryClient = useQueryClient();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we call useQueryClient inside the wrapper to get access to the client from the tree mounted by this test

};

interface addNotificationProps {
export interface AddNotificationProps {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

no big changes here, i just capitalised this, OCD

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors how the React Query client is created and wired through the app’s providers, adding mutation cache support for error notifications, and updates several tests to use the new useQueryClient pattern.

  • Extract createGetiQueryClient into its own function and add a MutationCache that triggers error notifications.
  • Refactor RequiredProviders and testing utilities to use PrefilledQueryClientProvider with the new client creation API.
  • Update tests to use useQueryClient instead of manually instantiating QueryClient, and rename notification prop types.

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web_ui/src/test-utils/required-providers-render.tsx Use PrefilledQueryClientProvider and extracted query client code
web_ui/src/routes/organizations/organizations-context.test.tsx Replace waitForElementToBeRemoved with waitFor, simplify setup
web_ui/src/providers/query-client-provider/query-client-provider.component.tsx Extract createGetiQueryClient, add mutationCache, refactor provider
web_ui/src/pages/media/hooks/media-delete/media-delete.hook.test.tsx Switch to useQueryClient hook, remove direct QueryClient usage
web_ui/src/notification/notification.component.tsx Rename and export AddNotificationProps
web_ui/src/core/jobs/hooks/utils.test.tsx Update tests to call useQueryClient for invalidation testing
Comments suppressed due to low confidence (1)

web_ui/src/core/jobs/hooks/utils.test.tsx:39

  • [nitpick] The variable queryClient here holds the hook result, not the client directly. Consider renaming it (e.g., hookResult) to avoid confusion.
        const queryClient = renderHookWithProviders(useQueryClient);

<ThemeProvider theme={defaultTheme}>
<NotificationProvider>
<Notifications />
<PrefilledQueryClientProvider featureFlags={featureFlags} profile={profile}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is the main change

Copy link
Contributor

@MarkRedeman MarkRedeman left a comment

Choose a reason for hiding this comment

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

I'm not sure if I fully understand the intend of this PR. I'll try to come back to this later

Comment on lines 109 to 111
if (customQueryClient) {
return customQueryClient;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels a bit odd to me, if a user needs a different query client then they can render the provider manually,

        <TanstackQueryClientProvider client={customQueryClient}>
            // ...
        </TanstackQueryClientProvider>

instead of relying on this wrapper.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, previous implementation quirks. Got rid of it now.

featureFlags?: CustomFeatureFlags;
profile?: OnboardingProfile | null;
}) => {
const { addNotification } = useNotification();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this notification behaviour in our tests? If we verify that createGetiQueryClient works as expected with a single unit test then I don't think we really need it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we can also just pass the ref. But what you mean here is that we dont need notification stuff on the tests, but i dont think that is true. We do need it and we test a lot of notifications. Once we remove the "addNotification(...)" from the consumers (since our QC will trigger them by default) we will still be able to test the notification presence.

<Notifications />
<PrefilledQueryClientProvider featureFlags={featureFlags} profile={profile}>
<ThemeProvider theme={defaultTheme}>
<Suspense fallback={<IntelBrandedLoading />}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why the extra suspense you might ask? Good question:

First, it's just for the tests so no danger there, secondly:

From google/copilot/chatgpt/testing:
"Sometimes, due to React’s rendering order or test environment quirks, the outer isn’t "ready" to catch suspensions from providers/components deep in the tree.
An inner ensures that any suspension in that subtree is always caught, preventing errors and showing the fallback as intended."

it('Should invalidate balance if feature flag is enabled', async () => {
const queryClient = new QueryClient();
queryClient.invalidateQueries = jest.fn();
let invalidateSpy: jest.SpyInstance | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed offline about these. I just "refactored" the tests to make it work with the new approach, but fundamentally they are not great tests in the sense that they test if react-query works instead of testing our app behavior AFTER react-query acts. So we should check if , once we do X, then Y happens, not that react-query called its internals correctly.

pplaskie
pplaskie previously approved these changes Jun 26, 2025
queryClient.invalidateQueries = mockSetInvalidateQueries;

const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
Copy link
Contributor

Choose a reason for hiding this comment

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

I think instead of creating second QueryClientProvider instance we could just leave the previous implementation of the RequiredProvidersProps which accepts optionally the queryClient. In case we want to mock queryClient, we create it using createGetiQueryClient and pass it to the hook/provider. For instance: useInvalidateBalanceOnNewJob(..., { providerProps: { queryClient }}).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants