diff --git a/packages/toolkit/src/query/fetchBaseQuery.ts b/packages/toolkit/src/query/fetchBaseQuery.ts index e8d152dbe3..1b5eb13da2 100644 --- a/packages/toolkit/src/query/fetchBaseQuery.ts +++ b/packages/toolkit/src/query/fetchBaseQuery.ts @@ -27,6 +27,13 @@ export interface FetchArgs extends CustomRequestInit { validateStatus?: (response: Response, body: any) => boolean } +/** + * A mini-wrapper that passes arguments straight through to + * {@link [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)}. + * Avoids storing `fetch` in a closure, in order to permit mocking/monkey-patching. + */ +const defaultFetchFn: typeof fetch = (...args) => fetch(...args) + const defaultValidateStatus = (response: Response) => response.status >= 200 && response.status <= 299 @@ -118,7 +125,7 @@ export type FetchBaseQueryMeta = { request: Request; response: Response } export function fetchBaseQuery({ baseUrl, prepareHeaders = (x) => x, - fetchFn = fetch, + fetchFn = defaultFetchFn, ...baseFetchOptions }: FetchBaseQueryArgs = {}): BaseQueryFn< string | FetchArgs, @@ -127,6 +134,11 @@ export function fetchBaseQuery({ {}, FetchBaseQueryMeta > { + if (typeof fetch === 'undefined' && fetchFn === defaultFetchFn) { + console.warn( + 'Warning: `fetch` is not available. Please supply a custom `fetchFn` property to use `fetchBaseQuery` on SSR environments.' + ) + } return async (arg, { signal, getState }) => { let { url, diff --git a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx index 0a3b4e03b0..50060c6cb9 100644 --- a/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx +++ b/packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx @@ -531,6 +531,34 @@ describe('fetchFn', () => { expect(request.url).toEqual(`${baseUrl}/echo?apple=fruit`) }) + + test('respects mocking window.fetch after a fetch base query is created', async () => { + const baseUrl = 'http://example.com' + const baseQuery = fetchBaseQuery({ baseUrl }) + + const fakeResponse = { + ok: true, + status: 200, + text: async () => `{ "url": "mock-return-url" }`, + clone: () => fakeResponse + } + + const spiedFetch = jest.spyOn(window, 'fetch'); + spiedFetch.mockResolvedValueOnce(fakeResponse as any); + + const { data } = await baseQuery( + { url: '/echo' }, + { + signal: new AbortController().signal, + dispatch: storeRef.store.dispatch, + getState: storeRef.store.getState, + }, + {} + ) + expect(data).toEqual({url: 'mock-return-url'}) + + spiedFetch.mockClear(); + }) }) describe('FormData', () => { diff --git a/yarn.lock b/yarn.lock index 09161940c4..3532fde762 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3384,9 +3384,12 @@ __metadata: resolution: "@examples-query-react/basic@workspace:examples/query/react/basic" dependencies: "@reduxjs/toolkit": ^1.6.0-rc.1 + "@testing-library/react": ^12.0.0 + "@types/jest": ^26.0.23 "@types/react": 17.0.0 "@types/react-dom": 17.0.0 "@types/react-redux": 7.1.9 + msw: ^0.30.0 react: 17.0.0 react-dom: 17.0.0 react-redux: 7.2.2 @@ -3965,6 +3968,19 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^27.0.6": + version: 27.0.6 + resolution: "@jest/types@npm:27.0.6" + dependencies: + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^16.0.0 + chalk: ^4.0.0 + checksum: 86c1d8707ffcf8fb3574fa6865e1768d771c83494df5bfdc81199cac81fdbe947c69a7c113d03b7bf21c926bbfefffcfe7253138d6e52bb61a1386e05aff2a6b + languageName: node + linkType: hard + "@manaflair/redux-batch@npm:^1.0.0": version: 1.0.0 resolution: "@manaflair/redux-batch@npm:1.0.0" @@ -4076,7 +4092,7 @@ __metadata: languageName: node linkType: hard -"@mswjs/cookies@npm:^0.1.4": +"@mswjs/cookies@npm:^0.1.4, @mswjs/cookies@npm:^0.1.5": version: 0.1.6 resolution: "@mswjs/cookies@npm:0.1.6" dependencies: @@ -4107,6 +4123,19 @@ __metadata: languageName: node linkType: hard +"@mswjs/interceptors@npm:^0.11.1": + version: 0.11.1 + resolution: "@mswjs/interceptors@npm:0.11.1" + dependencies: + "@open-draft/until": ^1.0.3 + debug: ^4.3.0 + headers-utils: ^3.0.2 + strict-event-emitter: ^0.2.0 + xmldom: ^0.6.0 + checksum: 8227109c7dacc4d1bc331ea7e45ca5a07196d2ab87c387d2225f2bee8cb3aad906afad4d62442e76852c5bd54b89ab0278ea6b4a0a6bab2eda76a338c1857815 + languageName: node + linkType: hard + "@mswjs/interceptors@npm:^0.8.0": version: 0.8.1 resolution: "@mswjs/interceptors@npm:0.8.1" @@ -4781,6 +4810,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^8.0.0": + version: 8.1.0 + resolution: "@testing-library/dom@npm:8.1.0" + dependencies: + "@babel/code-frame": ^7.10.4 + "@babel/runtime": ^7.12.5 + "@types/aria-query": ^4.2.0 + aria-query: ^4.2.2 + chalk: ^4.1.0 + dom-accessibility-api: ^0.5.6 + lz-string: ^1.4.4 + pretty-format: ^27.0.2 + checksum: 5b8c04123a50f2d3e01d2b1c8c929a009e83caca5b1813785330657d387914dedd87f0d158e94c6039b08d00c19a17a61a66fd9fca39cb67115718314dcced5a + languageName: node + linkType: hard + "@testing-library/react-hooks@npm:^5.1.2": version: 5.1.3 resolution: "@testing-library/react-hooks@npm:5.1.3" @@ -4817,6 +4862,19 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^12.0.0": + version: 12.0.0 + resolution: "@testing-library/react@npm:12.0.0" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^8.0.0 + peerDependencies: + react: "*" + react-dom: "*" + checksum: 2b5da0513f5de02636c1610625ca55222ea5c89ee271a9c9c3bcc1282010b9649b8168b8c84d8dc37a29daf0a8c909280ccaf8648dd09c19f2e2d640ea7f9459 + languageName: node + linkType: hard + "@testing-library/user-event@npm:^13.1.5": version: 13.1.9 resolution: "@testing-library/user-event@npm:13.1.9" @@ -5072,6 +5130,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^26.0.23": + version: 26.0.24 + resolution: "@types/jest@npm:26.0.24" + dependencies: + jest-diff: ^26.0.0 + pretty-format: ^26.0.0 + checksum: e5d3651c1edf197011d19ee8f4bacd52667fa673d1b2ffbfbce624208d643fd4372ebb31fa8a1d89843a7705f142e38076e25e1e7b67f3aacafd6ae8bc77cbcf + languageName: node + linkType: hard + "@types/js-levenshtein@npm:^1.1.0": version: 1.1.0 resolution: "@types/js-levenshtein@npm:1.1.0" @@ -5507,6 +5575,15 @@ __metadata: languageName: node linkType: hard +"@types/yargs@npm:^16.0.0": + version: 16.0.4 + resolution: "@types/yargs@npm:16.0.4" + dependencies: + "@types/yargs-parser": "*" + checksum: 929334a45c548f78e5aa756282b8abe17c0c7233eaaf414795808ab36a5f9957b3bc8c84151429d919fd2ac04ed2ce99853a85aea65823b4f669643656a3c65e + languageName: node + linkType: hard + "@types/yargs@npm:^16.0.1": version: 16.0.3 resolution: "@types/yargs@npm:16.0.3" @@ -6319,6 +6396,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 10b01465c7a49cbfcc055188e3b79b00db6283319bb53c0d20ca9ad114d1477d6f48c1d01a3ed9678f616566ec33f11116926dfaa162fa7be9ee7d5d2c2ea7e1 + languageName: node + linkType: hard + "anymatch@npm:^2.0.0": version: 2.0.0 resolution: "anymatch@npm:2.0.0" @@ -12703,6 +12787,28 @@ fsevents@^1.2.7: languageName: node linkType: hard +"inquirer@npm:^8.1.0": + version: 8.1.1 + resolution: "inquirer@npm:8.1.1" + dependencies: + ansi-escapes: ^4.2.1 + chalk: ^4.1.1 + cli-cursor: ^3.1.0 + cli-width: ^3.0.0 + external-editor: ^3.0.3 + figures: ^3.0.0 + lodash: ^4.17.21 + mute-stream: 0.0.8 + ora: ^5.3.0 + run-async: ^2.4.0 + rxjs: ^6.6.6 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + through: ^2.3.6 + checksum: e5cd5c1aa295970666009b555af2e954dc16144efb5fd5c08642653598f88f23a41dda9626c44bc5e9909d8269f5e3dcdddbe18512685d772b94743f263e0f6f + languageName: node + linkType: hard + "internal-ip@npm:^4.3.0": version: 4.3.0 resolution: "internal-ip@npm:4.3.0" @@ -13536,7 +13642,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"jest-diff@npm:^26.6.2": +"jest-diff@npm:^26.0.0, jest-diff@npm:^26.6.2": version: 26.6.2 resolution: "jest-diff@npm:26.6.2" dependencies: @@ -15512,6 +15618,35 @@ fsevents@^1.2.7: languageName: node linkType: hard +"msw@npm:^0.30.0": + version: 0.30.1 + resolution: "msw@npm:0.30.1" + dependencies: + "@mswjs/cookies": ^0.1.5 + "@mswjs/interceptors": ^0.11.1 + "@open-draft/until": ^1.0.3 + "@types/cookie": ^0.4.0 + "@types/inquirer": ^7.3.1 + "@types/js-levenshtein": ^1.1.0 + chalk: ^4.1.1 + chokidar: ^3.4.2 + cookie: ^0.4.1 + graphql: ^15.4.0 + headers-utils: ^3.0.2 + inquirer: ^8.1.0 + js-levenshtein: ^1.1.6 + node-fetch: ^2.6.1 + node-match-path: ^0.6.3 + statuses: ^2.0.0 + strict-event-emitter: ^0.2.0 + type-fest: ^1.1.3 + yargs: ^17.0.1 + bin: + msw: cli/index.js + checksum: 29706bdd694e98c5d536f12ebb1863d364e1643dccd9bc16e0ff4d8379f55f9e227d19b4dec18c8b070bdacc473efc9fcb370387620050760bda39085da5bf0a + languageName: node + linkType: hard + "multicast-dns-service-types@npm:^1.1.0": version: 1.1.0 resolution: "multicast-dns-service-types@npm:1.1.0" @@ -15717,7 +15852,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"node-match-path@npm:^0.6.1": +"node-match-path@npm:^0.6.1, node-match-path@npm:^0.6.3": version: 0.6.3 resolution: "node-match-path@npm:0.6.3" checksum: f93cfdbc7035485c2211598b9000eabe28bf8c8d234ebb165ed7eeb7070b50f5fba0871f3fa6acca2604cce38ac3e7e333ef6a3e8e5ba82e40a0e38d3748e27d @@ -16146,6 +16281,23 @@ fsevents@^1.2.7: languageName: node linkType: hard +"ora@npm:^5.3.0": + version: 5.4.1 + resolution: "ora@npm:5.4.1" + dependencies: + bl: ^4.1.0 + chalk: ^4.1.0 + cli-cursor: ^3.1.0 + cli-spinners: ^2.5.0 + is-interactive: ^1.0.0 + is-unicode-supported: ^0.1.0 + log-symbols: ^4.1.0 + strip-ansi: ^6.0.0 + wcwidth: ^1.0.1 + checksum: 589ac687d6dfbac970f1bf4ec94f3e1cfc9e2a4dd4727a0c60dfcdef138f59b5de5694447881fa5a2793a91f0b2619a96bdd3d5425be30d155540abbe1df95b9 + languageName: node + linkType: hard + "ora@npm:^5.4.0": version: 5.4.0 resolution: "ora@npm:5.4.0" @@ -18121,7 +18273,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"pretty-format@npm:^26.6.0, pretty-format@npm:^26.6.2": +"pretty-format@npm:^26.0.0, pretty-format@npm:^26.6.0, pretty-format@npm:^26.6.2": version: 26.6.2 resolution: "pretty-format@npm:26.6.2" dependencies: @@ -18133,6 +18285,18 @@ fsevents@^1.2.7: languageName: node linkType: hard +"pretty-format@npm:^27.0.2": + version: 27.0.6 + resolution: "pretty-format@npm:27.0.6" + dependencies: + "@jest/types": ^27.0.6 + ansi-regex: ^5.0.0 + ansi-styles: ^5.0.0 + react-is: ^17.0.1 + checksum: 29baf8d88a35c04ef81e310e3e239c6fe4f5b0827c9bd8f660df78e94632a9677bd113a64be78cc95c52945a4369b85828f32559cfd86c1b591aa3cc39d0798d + languageName: node + linkType: hard + "pretty-time@npm:^1.1.0": version: 1.1.0 resolution: "pretty-time@npm:1.1.0" @@ -19397,9 +19561,9 @@ fsevents@^1.2.7: languageName: node linkType: hard -"remark-typescript-tools@npm:^1.0.7": - version: 1.0.7 - resolution: "remark-typescript-tools@npm:1.0.7" +"remark-typescript-tools@npm:^1.0.8": + version: 1.0.8 + resolution: "remark-typescript-tools@npm:1.0.8" dependencies: "@microsoft/tsdoc": ^0.12.21 prettier: ^2.1.1 @@ -19407,7 +19571,7 @@ fsevents@^1.2.7: unist-util-visit: ^2.0.3 peerDependencies: typescript: ">=4.0" - checksum: ea4ae8e383b1f38b3de63deda9c9f4baae10ac58a7f164a32e50f8ac2866a9df5cc364be4334b5d7e4ab8881498f3e8660b45797b5cde1199a67cf86a1f10088 + checksum: 2f63dfe6c0cc3a12d0d91c011df9f0b6d828693cff35628190ecd218dcb6b5b537001a3b4f8eccf18c9bc1f0584c64a72d9a0a757055fa92849ed0f6f2dd5e96 languageName: node linkType: hard @@ -19937,7 +20101,7 @@ resolve@~1.19.0: languageName: node linkType: hard -"rxjs@npm:^6.4.0, rxjs@npm:^6.6.0, rxjs@npm:^6.6.2, rxjs@npm:^6.6.3": +"rxjs@npm:^6.4.0, rxjs@npm:^6.6.0, rxjs@npm:^6.6.2, rxjs@npm:^6.6.3, rxjs@npm:^6.6.6": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -21914,6 +22078,13 @@ resolve@~1.19.0: languageName: node linkType: hard +"type-fest@npm:^1.1.3": + version: 1.2.2 + resolution: "type-fest@npm:1.2.2" + checksum: 1f5510d0afa1f17523ba7f320c6e3f222fe6c1f9d15689a69460ba7fc6ae24150958ddca7e509f9abc7ec456ed187dbf2da019fd308788d53094dc25f56eda59 + languageName: node + linkType: hard + "type-is@npm:~1.6.17, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -23046,7 +23217,7 @@ resolve@~1.19.0: react: 17.0.2 react-dom: 17.0.2 react-lite-youtube-embed: ^2.0.3 - remark-typescript-tools: ^1.0.7 + remark-typescript-tools: ^1.0.8 typescript: ~4.2.4 languageName: unknown linkType: soft @@ -23499,6 +23670,13 @@ resolve@~1.19.0: languageName: node linkType: hard +"xmldom@npm:^0.6.0": + version: 0.6.0 + resolution: "xmldom@npm:0.6.0" + checksum: cca0cddf6f95e9bfa2432a9a819f97b840e6b36fad8017833f5c7d298b4fd69ef2e3e7b5dd68f1e53ec5dd4075bfc8f68e425b06928adc0a21c9bacf0af02b0c + languageName: node + linkType: hard + "xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" @@ -23620,6 +23798,21 @@ resolve@~1.19.0: languageName: node linkType: hard +"yargs@npm:^17.0.1": + version: 17.0.1 + resolution: "yargs@npm:17.0.1" + dependencies: + cliui: ^7.0.2 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.0 + y18n: ^5.0.5 + yargs-parser: ^20.2.2 + checksum: a7969b48d2dea129a7d4fcc3f13e88d4f94bacbd24f720b2ce19946fa9facc42cfed89c059d953091241f4e9e9000ed9dbf86e4bb4b6ceb3a26af10ddebdd0b2 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"