diff --git a/docs/docs/Usage/upgrade-guide.md b/docs/docs/Usage/upgrade-guide.md index e1507cff..8b1b51ac 100644 --- a/docs/docs/Usage/upgrade-guide.md +++ b/docs/docs/Usage/upgrade-guide.md @@ -8,19 +8,27 @@ The [@fetch-mock/codemods](https://www.npmjs.com/package/@fetch-mock/codemods) l ## Summary of changes +### Uses global, native `fetch` implementation in all environments + +Previously this was true in browsers, but in node.js the node-fetch library was used. + ### `.mock()` method removed This combined adding a route with mocking the global instance of `fetch`. These are now split into 2 methods: `.route()` and `.mockGlobal()`. -## Reset methods changed +### Reset methods changed `.reset()`, `.restore()`, `.resetBehavior()` and `.resetHistory()` have been removed and replaced with [methods that are more granular and clearly named](/fetch-mock/docs/API/resetting). Note that the [jest](/fetch-mock/docs/wrappers/jest) and [vitest](/fetch-mock/docs/wrappers/vitest) wrappers for fetch-mock still implement `.mockClear()`, `mockReset()` and `mockRestore()`. -## Call history methods changed +### Call history methods changed The filtering behaviour has been rewritten around named routes, and methods return CallLog objects that contain far more metadata about the call. [Call history docs](/fetch-mock/docs/API/CallHistory) -## Some convenience routing methods removed +### Relative URLs not permitted by default in Node.js + +A consequence of shifting to use the native implementation of the URL class is that relative URLs are no longer permissable when running tests in node.js, but [this can easily be enabled](https://www.wheresrhys.co.uk/fetch-mock/docs/Usage/configuration#allowrelativeurls). + +### Some convenience routing methods removed `getOnce()` and `getAnyOnce()` have been removed, but the behaviour can still be implemented by the user as follows: @@ -29,13 +37,13 @@ The filtering behaviour has been rewritten around named routes, and methods retu The same is true for `postOnce()`, `deleteOnce()` etc. -## Options removed +### Options removed - `overwriteRoutes` - this reflects that multiple routes using the same underlying matcher but different options no longer throw an error. - `warnOnFallback` - given the improved state of node.js debugging tools compared to when fetch-mock was first written, this debugging utilty has been removed. - `sendAsJson` - fetch-mock@12 implements streams more robustly than previous options, so the user no longer needs to flag when an object response should be converted to JSON. - `fallbackToNetwork` - The [`spyGlobal()` method](/fetch-mock/docs/API/mocking-and-spying#spyglobal) should now be used. -## `sandbox()` method removed +### `sandbox()` method removed This was principally used when mocking node-fetch referenced as a local variable. Given that `fetch` is now available as a native global it's less useful and has been removed. If necessary to mock a local instance of node-fetch use [`.fetchHandler`](/fetch-mock/docs/API/mocking-and-spying#fetchhandler) diff --git a/packages/fetch-mock/src/RequestUtils.ts b/packages/fetch-mock/src/RequestUtils.ts index bba2dc63..51ef08fb 100644 --- a/packages/fetch-mock/src/RequestUtils.ts +++ b/packages/fetch-mock/src/RequestUtils.ts @@ -17,7 +17,7 @@ export type NormalizedRequestOptions = export function hasCredentialsInUrl(url: string): boolean { const urlObject = new URL( url, - protocolRelativeUrlRX.test(url) ? 'http://dummy' : undefined, + !absoluteUrlRX.test(url) ? 'http://dummy' : undefined, ); return Boolean(urlObject.username || urlObject.password); } diff --git a/packages/fetch-mock/src/__tests__/router-integration.test.js b/packages/fetch-mock/src/__tests__/router-integration.test.js index 2eb37f40..4e83b12d 100644 --- a/packages/fetch-mock/src/__tests__/router-integration.test.js +++ b/packages/fetch-mock/src/__tests__/router-integration.test.js @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; import fetchMock from '../FetchMock'; describe('router integration', () => { + const isBrowser = Boolean(globalThis.location); it('matchurls when called with Request', async () => { const fm = fetchMock.createInstance(); fm.post('http://a.com/', 200).catch(); @@ -65,6 +66,14 @@ describe('router integration', () => { ), ).resolves.not.toThrow(); }); + if (!isBrowser) { + it('can match relative urls when allowRelativeUrls option is true', async () => { + const fm = fetchMock.createInstance(); + fm.route('/path', 200, { allowRelativeUrls: true }); + const res = await fm.fetchHandler('/path'); + expect(res.status).toEqual(200); + }); + } }); describe('user defined matchers', () => { it('match on sync property', async () => { diff --git a/packages/fetch-mock/src/__tests__/spec-compliance.test.js b/packages/fetch-mock/src/__tests__/spec-compliance.test.js index 994b82a6..57bb3825 100644 --- a/packages/fetch-mock/src/__tests__/spec-compliance.test.js +++ b/packages/fetch-mock/src/__tests__/spec-compliance.test.js @@ -23,6 +23,15 @@ describe('Spec compliance', () => { ), ); }); + it('rejects on protocol agnostic url containing credentials', async () => { + await expect( + fetchMock.fetchHandler('//user:password@a.com'), + ).rejects.toThrow( + new TypeError( + 'Request cannot be constructed from a URL that includes credentials: //user:password@a.com/', + ), + ); + }); it('reject if the request method is GET or HEAD and the body is non-null.', async () => { await expect( fetchMock.fetchHandler('http://a.com', { body: 'a' }),