diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index f9273b812c..f8e3b33a54 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -510,10 +510,7 @@ export function buildThunks< endpointDefinition try { - let transformResponse = getTransformCallbackForEndpoint( - endpointDefinition, - 'transformResponse', - ) + let transformResponse: TransformCallback = defaultTransformResponse const baseQueryApi = { signal, @@ -587,6 +584,13 @@ export function buildThunks< // upsertQueryData relies on this to pass in the user-provided value result = forceQueryFn() } else if (endpointDefinition.query) { + // We should only run `transformResponse` when the endpoint has a `query` method, + // and we're not doing an `upsertQueryData`. + transformResponse = getTransformCallbackForEndpoint( + endpointDefinition, + 'transformResponse', + ) + result = await baseQuery( endpointDefinition.query(finalQueryArg as any), baseQueryApi, diff --git a/packages/toolkit/src/query/tests/buildThunks.test.tsx b/packages/toolkit/src/query/tests/buildThunks.test.tsx index 786bce700a..f197afcc90 100644 --- a/packages/toolkit/src/query/tests/buildThunks.test.tsx +++ b/packages/toolkit/src/query/tests/buildThunks.test.tsx @@ -4,71 +4,115 @@ import { renderHook, waitFor } from '@testing-library/react' import { actionsReducer, withProvider } from '../../tests/utils/helpers' import type { BaseQueryApi } from '../baseQueryTypes' -test('handles a non-async baseQuery without error', async () => { - const baseQuery = (args?: any) => ({ data: args }) - const api = createApi({ - baseQuery, - endpoints: (build) => ({ - getUser: build.query({ - query(id) { - return { url: `user/${id}` } - }, +describe('baseline thunk behavior', () => { + test('handles a non-async baseQuery without error', async () => { + const baseQuery = (args?: any) => ({ data: args }) + const api = createApi({ + baseQuery, + endpoints: (build) => ({ + getUser: build.query({ + query(id) { + return { url: `user/${id}` } + }, + }), }), - }), - }) - const { getUser } = api.endpoints - const store = configureStore({ - reducer: { - [api.reducerPath]: api.reducer, - }, - middleware: (gDM) => gDM().concat(api.middleware), - }) - - const promise = store.dispatch(getUser.initiate(1)) - const { data } = await promise + }) + const { getUser } = api.endpoints + const store = configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + }, + middleware: (gDM) => gDM().concat(api.middleware), + }) - expect(data).toEqual({ - url: 'user/1', - }) + const promise = store.dispatch(getUser.initiate(1)) + const { data } = await promise - const storeResult = getUser.select(1)(store.getState()) - expect(storeResult).toEqual({ - data: { + expect(data).toEqual({ url: 'user/1', - }, - endpointName: 'getUser', - isError: false, - isLoading: false, - isSuccess: true, - isUninitialized: false, - originalArgs: 1, - requestId: expect.any(String), - status: 'fulfilled', - startedTimeStamp: expect.any(Number), - fulfilledTimeStamp: expect.any(Number), + }) + + const storeResult = getUser.select(1)(store.getState()) + expect(storeResult).toEqual({ + data: { + url: 'user/1', + }, + endpointName: 'getUser', + isError: false, + isLoading: false, + isSuccess: true, + isUninitialized: false, + originalArgs: 1, + requestId: expect.any(String), + status: 'fulfilled', + startedTimeStamp: expect.any(Number), + fulfilledTimeStamp: expect.any(Number), + }) }) -}) -test('passes the extraArgument property to the baseQueryApi', async () => { - const baseQuery = (_args: any, api: BaseQueryApi) => ({ data: api.extra }) - const api = createApi({ - baseQuery, - endpoints: (build) => ({ - getUser: build.query({ - query: () => '', + test('passes the extraArgument property to the baseQueryApi', async () => { + const baseQuery = (_args: any, api: BaseQueryApi) => ({ data: api.extra }) + const api = createApi({ + baseQuery, + endpoints: (build) => ({ + getUser: build.query({ + query: () => '', + }), }), - }), + }) + const store = configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + }, + middleware: (gDM) => + gDM({ thunk: { extraArgument: 'cakes' } }).concat(api.middleware), + }) + const { getUser } = api.endpoints + const { data } = await store.dispatch(getUser.initiate()) + expect(data).toBe('cakes') }) - const store = configureStore({ - reducer: { - [api.reducerPath]: api.reducer, - }, - middleware: (gDM) => - gDM({ thunk: { extraArgument: 'cakes' } }).concat(api.middleware), + + test('only triggers transformResponse when a query method is actually used', async () => { + const baseQuery = (args?: any) => ({ data: args }) + const transformResponse = vi.fn((response: any) => response) + const api = createApi({ + baseQuery, + endpoints: (build) => ({ + hasQuery: build.query({ + query: (arg) => 'test', + transformResponse, + }), + hasQueryFn: build.query( + // @ts-expect-error + { + queryFn: () => ({ data: 'test' }), + transformResponse, + }, + ), + }), + }) + + const store = configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + }, + middleware: (gDM) => + gDM({ thunk: { extraArgument: 'cakes' } }).concat(api.middleware), + }) + + await store.dispatch(api.util.upsertQueryData('hasQuery', 'a', 'test')) + expect(transformResponse).not.toHaveBeenCalled() + + transformResponse.mockReset() + + await store.dispatch(api.endpoints.hasQuery.initiate('b')) + expect(transformResponse).toHaveBeenCalledTimes(1) + + transformResponse.mockReset() + + await store.dispatch(api.endpoints.hasQueryFn.initiate()) + expect(transformResponse).not.toHaveBeenCalled() }) - const { getUser } = api.endpoints - const { data } = await store.dispatch(getUser.initiate()) - expect(data).toBe('cakes') }) describe('re-triggering behavior on arg change', () => {