diff --git a/docs/api.md b/docs/api.md index fe052cf7..0ee88b0b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -4,6 +4,7 @@ - API documentation - [Troubleshooting](/fetch-mock/troubleshooting) - [Examples](/fetch-mock/examples) + * [V6 - V7 upgrade guide](/fetch-mock/v6-v7-upgrade) * [V6 docs](/fetch-mock/v6) * [V5 - V6 upgrade guide](/fetch-mock/v5-v6-upgrade) @@ -14,65 +15,73 @@ ## Mocking calls to `fetch` #### `mock(matcher, response, options)` or `mock(options)` -Replaces `fetch` with a stub which records its calls, grouped by route, and optionally returns a mocked `Response` object or passes the call through to `fetch()`. Calls to `.mock()` can be chained. *Note that once mocked, `fetch` will error on any unmatched calls. Use `.spy()` or `.catch()` to handle unmocked calls more gracefully* -* `matcher`: Condition for selecting which requests to mock. (For matching based on headers, query strings or other `fetch` options see the `options` parameter documented below). Accepts any of the following: - * `string`: Either - * an exact url to match e.g. 'http://www.site.com/page.html' - * `*` to match any url - * `begin:http://www.site.com/` to match urls beginning with a string - * `end:.jpg` to match urls ending with a string - * `glob:http://*.*` to match glob patterns - * `express:/user/:user` to match [express style paths](https://www.npmjs.com/package/path-to-regexp) - * `RegExp`: A regular expression to test the url against - * `Function(url, opts)`: A function (returning a Boolean) that is passed the url and opts `fetch()` is called with (or, if `fetch()` was called with one, the `Request` instance) - -* `response`: Configures the http response returned by the mock. Can take any of the following values (or be a `Promise` for any of them, enabling full control when testing race conditions etc.) - * `Response`: A `Response` instance - will be used unaltered - * `number`: Creates a response with this status - * `string`: Creates a 200 response with the string as the response body - * `configObject` If an object _does not contain_ any properties aside from those listed below it is treated as config to build a `Response` - * `body`: Set the response body (`string` or `object`) - * `status`: Set the response status (default `200`) - * `headers`: Set the response headers. (`object`) - * `throws`: If this property is present then a the value of `throws` is thrown - * `sendAsJson`: This property determines whether or not the request body should be converted to `JSON` before being sent (defaults to `true`). - * `includeContentLength`: Set this property to true to automatically add the `content-length` header (defaults to `true`). - * `redirectUrl`: *experimental* the url the response should be from (to imitate followed redirects - will set `redirected: true` on the response) - * `object`: All objects that do not meet the criteria above are converted to `JSON` and returned as the body of a 200 response. - * `Function(url, opts)`: A function that is passed the url and opts `fetch()` is called with and that returns any of the responses listed above (or a `Promise` for any of them) -* `options`: A configuration object with all/additional properties to define a route to mock - * `name`: A unique string naming the route. Used to subsequently retrieve references to the calls, grouped by name. Defaults to `matcher.toString()` - * `method`: http method to match - * `headers`: key/value map of headers to match - * `query`: key/value map of query strings to match, in any order - * `matcher`: as specified above - * `response`: as specified above - * `repeat`: An integer, `n`, limiting the number of times the matcher can be used. If the route has already been called `n` times the route will be ignored and the call to `fetch()` will fall through to be handled by any other routes defined (which may eventually result in an error if nothing matches it) - * `overwriteRoutes`: If the route you're adding clashes with an existing route, setting `true` here will overwrite the clashing route, `false` will add another route to the stack which will be used as a fallback (useful when using the `repeat` option). Adding a clashing route without specifying this option will throw an error. It can also be set as a global option (see the **Config** section below) + +Replaces `fetch` with a stub which records its calls, grouped by route, and optionally returns a mocked `Response` object or passes the call through to `fetch()`. Calls to `.mock()` can be chained. _Note that once mocked, `fetch` will error on any unmatched calls. Use `.spy()` or `.catch()` to handle unmocked calls more gracefully_ + +- `matcher`: Condition for selecting which requests to mock. (For matching based on headers, query strings or other `fetch` options see the `options` parameter documented below). Accepts any of the following: + + - `string`: Either + - an exact url to match e.g. 'http://www.site.com/page.html' + - `*` to match any url + - `begin:http://www.site.com/` to match urls beginning with a string + - `end:.jpg` to match urls ending with a string + - `glob:http://*.*` to match glob patterns + - `express:/user/:user` to match [express style paths](https://www.npmjs.com/package/path-to-regexp) + - `RegExp`: A regular expression to test the url against + - `Function(url, opts)`: A function (returning a Boolean) that is passed the url and opts `fetch()` is called with (or, if `fetch()` was called with one, the `Request` instance) + +- `response`: Configures the http response returned by the mock. Can take any of the following values (or be a `Promise` for any of them, enabling full control when testing race conditions etc.) + - `Response`: A `Response` instance - will be used unaltered + - `number`: Creates a response with this status + - `string`: Creates a 200 response with the string as the response body + - `configObject` If an object _does not contain_ any properties aside from those listed below it is treated as config to build a `Response` + - `body`: Set the response body (`string` or `object`) + - `status`: Set the response status (default `200`) + - `headers`: Set the response headers. (`object`) + - `throws`: If this property is present then a the value of `throws` is thrown + - `sendAsJson`: This property determines whether or not the request body should be converted to `JSON` before being sent (defaults to `true`). + - `includeContentLength`: Set this property to true to automatically add the `content-length` header (defaults to `true`). + - `redirectUrl`: _experimental_ the url the response should be from (to imitate followed redirects - will set `redirected: true` on the response) + - `object`: All objects that do not meet the criteria above are converted to `JSON` and returned as the body of a 200 response. + - `Function(url, opts)`: A function that is passed the url and opts `fetch()` is called with and that returns any of the responses listed above (or a `Promise` for any of them) +- `options`: A configuration object with all/additional properties to define a route to mock + - `name`: A unique string naming the route. Used to subsequently retrieve references to the calls, grouped by name. Defaults to `matcher.toString()` + - `method`: http method to match + - `headers`: key/value map of headers to match + - `query`: key/value map of query strings to match, in any order + - `matcher`: as specified above + - `response`: as specified above + - `repeat`: An integer, `n`, limiting the number of times the matcher can be used. If the route has already been called `n` times the route will be ignored and the call to `fetch()` will fall through to be handled by any other routes defined (which may eventually result in an error if nothing matches it) + - `overwriteRoutes`: If the route you're adding clashes with an existing route, setting `true` here will overwrite the clashing route, `false` will add another route to the stack which will be used as a fallback (useful when using the `repeat` option). Adding a clashing route without specifying this option will throw an error. It can also be set as a global option (see the **Config** section below) #### `sandbox()` + This returns a drop-in mock for fetch which can be passed to other mocking libraries. It implements the full fetch-mock api and maintains its own state independent of other instances, so tests can be run in parallel. e.g. #### `once()` + Shorthand for `mock()` which limits to being called one time only. (see `repeat` option above) #### `get()`, `post()`, `put()`, `delete()`, `head()`, `patch()` -Shorthands for `mock()` restricted to a particular method *Tip: if you use some other method a lot you can easily define your own shorthands e.g.:* + +Shorthands for `mock()` restricted to a particular method _Tip: if you use some other method a lot you can easily define your own shorthands e.g.:_ ``` fetchMock.purge = function (matcher, response, options) { return this.mock(matcher, response, Object.assign({}, options, {method: 'PURGE'})); } - ``` #### `getOnce()`, `postOnce()`, `putOnce()`, `deleteOnce()`, `headOnce()`, `patchOnce()` + Shorthands for `mock()` restricted to a particular method and that will only respond once #### `catch(response)` -This is used to define how to respond to calls to fetch that don't match any of the defined mocks. It accepts the same types of response as a normal call to `.mock()`. It can also take an arbitrary function to completely customise behaviour of unmatched calls. It is chainable and can be called before or after other calls to `.mock()`. If `.catch() ` is called without any parameters then every unmatched call will receive a `200` response + +This is used to define how to respond to calls to fetch that don't match any of the defined mocks. It accepts the same types of response as a normal call to `.mock()`. It can also take an arbitrary function to completely customise behaviour of unmatched calls. It is chainable and can be called before or after other calls to `.mock()`. If `.catch()` is called without any parameters then every unmatched call will receive a `200` response #### `spy()` + Similar to `catch()`, this records the call history of unmatched calls, but instead of responding with a stubbed response, the request is passed through to native `fetch()` and is allowed to communicate over the network. To use `.spy()` on a sandboxed `fetchMock`, `fetchMock.config.fetch` must be set to a reference to the `fetch` implementation you use in your code. @@ -80,60 +89,72 @@ To use `.spy()` on a sandboxed `fetchMock`, `fetchMock.config.fetch` must be set ``` fetchMock.sandbox().mock('http://domain.com', 200) ``` + Existing sandboxed `fetchMock`s can also have `.sandbox()` called on them, thus building mocks that inherit some settings from a parent mock #### `restore()` + Chainable method that restores `fetch()` to its unstubbed state and clears all data recorded for its calls. #### `reset()` -Chainable method that clears all data recorded for `fetch()`'s calls. *It will not restore fetch to its default implementation* -*Note that `restore()` and `reset()` are both bound to fetchMock, and can be used directly as callbacks e.g. `afterEach(fetchMock.restore)` will work just fine. There is no need for `afterEach(function () {fetchMock.restore()})`* +Chainable method that clears all data recorded for `fetch()`'s calls. _It will not restore fetch to its default implementation_ + +_Note that `restore()` and `reset()` are both bound to fetchMock, and can be used directly as callbacks e.g. `afterEach(fetchMock.restore)` will work just fine. There is no need for `afterEach(function () {fetchMock.restore()})`_ ## Inspecting how `fetch()` has been called ### Filtering + Most of the methods below accept two parameters, `(filter, method)` + - `filter` Enables filtering fetch calls for the most commonly use cases. It can be: - - the name of a route - - The value of `matcher` or `matcher.toString()` for any unnamed route. You _can_ pass in the original regex or function as a matcher, but they will be converted to strings and used to look up values in fetch-mock's internal maps of calls, _not_ used as regexes or functions executed on teh url - - If `filter` is a string, and it does not match any routes, it is asumed the string is a url, and calls to `fetch` made with that url are returned - - `true` for matched calls only - - `false` for unmatched calls only - - `undefined` for all calls to fetch + - the name of a route + - The value of `matcher` or `matcher.toString()` for any unnamed route. You _can_ pass in the original regex or function as a matcher, but they will be converted to strings and used to look up values in fetch-mock's internal maps of calls, _not_ used as regexes or functions executed on teh url + - If `filter` is a string, and it does not match any routes, it is asumed the string is a url, and calls to `fetch` made with that url are returned + - `true` for matched calls only + - `false` for unmatched calls only + - `undefined` for all calls to fetch - `method` A http method to filter by #### `called(filter, method)` + Returns a Boolean indicating whether fetch was called and a route was matched. If `filter` is specified it only returns `true` if that particular route was matched. #### `done(filter, method)` + Returns a Boolean indicating whether fetch was called the expected number of times (or at least once if the route defines no expectation is set for the route). _Unlike the other methods for inspecting calls, unmatched calls are irrelevant_. Therefore, if no `filter` is passed, `done()` returns `true` if every route has been called the number of expected times. #### `calls(filter, method)` + Returns an array of all calls to fetch matchingthe given filters. Each call is returned as an array of length 2, `[url, options]`. If `fetch` was called using a `Request` instance, thsi will be available as a `request` property on this array. #### `lastCall(filter, method)` + Returns the arguments for the last matched call to fetch #### `lastUrl(filter, method)` + Returns the url for the last matched call to fetch. When `fetch` was last called using a `Request` instance, the url will be extracted from this #### `lastOptions(filter, method)` + Returns the options for the last matched call to fetch. When `fetch` was last called using a `Request` instance, the entire `Request` instance will be returned #### `flush()` + Returns a `Promise` that resolves once all fetches handled by fetch-mock have resolved. Useful for testing code that uses `fetch` but doesn't return a promise. ## Config On either the global or sandboxed `fetchMock` instances, the following config options can be set by setting properties on `fetchMock.config`. Many can also be set on individual calls to `.mock()`. -* `sendAsJson` [default `true`] - by default fetchMock will convert objects to JSON before sending. This is overrideable from each call but for some scenarios e.g. when dealing with a lot of array buffers, it can be useful to default to `false` -* `includeContentLength` [default `true`]: When set to true this will make fetchMock automatically add the `content-length` header. This is especially useful when combined with `sendAsJson` because then fetchMock does the conversion to JSON for you and knows the resulting length so you don’t have to compute this yourself by basically doing the same conversion to JSON. -* `fallbackToNetwork` [default `false`] If true then unmatched calls will transparently fall through to the network, if false an error will be thrown. If set to `always`, all calls will fall through, effectively disabling fetch-mock. to Within individual tests `.catch()` and `spy()` can be used for fine-grained control of this -* `overwriteRoutes`: If a new route clashes with an existing route, setting `true` here will overwrite the clashing route, `false` will add another route to the stack which will be used as a fallback (useful when using the `repeat` option). Adding a clashing route without specifying this option will throw an error. -* `warnOnFallback` [default `true`] If true then any unmatched calls that are caught by a fallback handler (either the network or a custom function set using `catch()`) will emit warnings -* `Headers`,`Request`,`Response`,`Promise`, `fetch` -When using non standard fetch (e.g. a ponyfill, or aversion of `node-fetch` other than the one bundled with `fetch-mock`) or an alternative Promise implementation, this will configure fetch-mock to use your chosen implementations. -Note that `Object.assign(fetchMock.config, require('fetch-ponyfill')())` will configure fetch-mock to use all of fetch-ponyfill's classes. In most cases, it should only be necessary to set this once before any tests run. +- `sendAsJson` [default `true`] - by default fetchMock will convert objects to JSON before sending. This is overrideable from each call but for some scenarios e.g. when dealing with a lot of array buffers, it can be useful to default to `false` +- `includeContentLength` [default `true`]: When set to true this will make fetchMock automatically add the `content-length` header. This is especially useful when combined with `sendAsJson` because then fetchMock does the conversion to JSON for you and knows the resulting length so you don’t have to compute this yourself by basically doing the same conversion to JSON. +- `fallbackToNetwork` [default `false`] If true then unmatched calls will transparently fall through to the network, if false an error will be thrown. If set to `always`, all calls will fall through, effectively disabling fetch-mock. to Within individual tests `.catch()` and `spy()` can be used for fine-grained control of this +- `overwriteRoutes`: If a new route clashes with an existing route, setting `true` here will overwrite the clashing route, `false` will add another route to the stack which will be used as a fallback (useful when using the `repeat` option). Adding a clashing route without specifying this option will throw an error. +- `warnOnFallback` [default `true`] If true then any unmatched calls that are caught by a fallback handler (either the network or a custom function set using `catch()`) will emit warnings +- `Headers`,`Request`,`Response`,`Promise`, `fetch` + When using non standard fetch (e.g. a ponyfill, or aversion of `node-fetch` other than the one bundled with `fetch-mock`) or an alternative Promise implementation, this will configure fetch-mock to use your chosen implementations. +Note that `Object.assign(fetchMock.config, require('fetch-ponyfill')())` will configure fetch-mock to use all of fetch-ponyfill's classes. In most cases, it should only be necessary to set this once before any tests run. diff --git a/docs/examples.md b/docs/examples.md index 4b4b9b9c..c4dff841 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -6,4 +6,5 @@ - Examples # Examples -//todo \ No newline at end of file + +//todo diff --git a/docs/index.md b/docs/index.md index 8cfd4c52..46d93df4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,14 +10,15 @@ As well as shorthand methods for the simplest use cases, it offers a flexible AP I devote a lot of time to maintaining fetch-mock for free. I don't ask for payment, but am raising money for a refugee charity - please consider donating - ## These docs are for v7 + - Introduction - [Quickstart](/fetch-mock/quickstart) - [Installation and usage ](/fetch-mock/installation) - [API documentation](/fetch-mock/api) - [Troubleshooting](/fetch-mock/troubleshooting) - [Examples](/fetch-mock/examples) + * [V6 - V7 upgrade guide](/fetch-mock/v6-v7-upgrade) * [V6 docs](/fetch-mock/v6) * [V5 - V6 upgrade guide](/fetch-mock/v5-v6-upgrade) diff --git a/docs/installation.md b/docs/installation.md index 7eabd0aa..bea42bbc 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,28 +1,31 @@ - [Introduction](/fetch-mock) - [Quickstart](/fetch-mock/quickstart) -- Installation and usage +- Installation and usage - [API documentation](/fetch-mock/api) - [Troubleshooting](/fetch-mock/troubleshooting) - [Examples](/fetch-mock/examples) # Installation + Install fetch-mock using `npm install --save-dev fetch-mock` In most environments use `const fetchMock = require('fetch-mock')` to use it in your code. Some exceptions include: -* If your client-side code or tests do not use a loader that respects the browser field of `package.json` use `require('fetch-mock/es5/client')`. -* If you need to use fetch-mock without commonjs, you can include the precompiled `node_modules/fetch-mock/es5/client-bundle.js` in a script tag. This loads fetch-mock into the `fetchMock` global variable. -* For server side tests running in nodejs 6 or lower use `require('fetch-mock/es5/server')`. You will also need to `npm i -D babel-polyfill` +- If your client-side code or tests do not use a loader that respects the browser field of `package.json` use `require('fetch-mock/es5/client')`. +- If you need to use fetch-mock without commonjs, you can include the precompiled `node_modules/fetch-mock/es5/client-bundle.js` in a script tag. This loads fetch-mock into the `fetchMock` global variable. +- For server side tests running in nodejs 6 or lower use `require('fetch-mock/es5/server')`. You will also need to `npm i -D babel-polyfill` ## Global fetch -By default fetch-mock assumes `fetch` is a global so once you've required fetch-mock refer to the quickstart and api docs. + +By default fetch-mock assumes `fetch` is a global so once you've required fetch-mock refer to the quickstart and api docs. ### Polyfilling fetch + Many older browsers will require polyfilling the `fetch` global -* In nodejs `require('isomorphic-fetch')` before any of your tests. -* In the browser `require('isomorphic-fetch')` can also be used, but it may be easier to `npm install whatwg-fetch` (the module isomorphic-fetch is built around) and load `./node_modules/whatwg-fetch/fetch.js` directly into the page, either in a script tag or by referencing in your test runner config. -* When using karma-webpack it's best not to use the `webpack.ProvidePlugin` for this. Instead just add `node_modules/whatwg-fetch/fetch.js` to your list of files to include, or require it directly into your tests before requiring fetch-mock. +- In nodejs `require('isomorphic-fetch')` before any of your tests. +- In the browser `require('isomorphic-fetch')` can also be used, but it may be easier to `npm install whatwg-fetch` (the module isomorphic-fetch is built around) and load `./node_modules/whatwg-fetch/fetch.js` directly into the page, either in a script tag or by referencing in your test runner config. +- When using karma-webpack it's best not to use the `webpack.ProvidePlugin` for this. Instead just add `node_modules/whatwg-fetch/fetch.js` to your list of files to include, or require it directly into your tests before requiring fetch-mock. ## Non-global fetch @@ -36,7 +39,8 @@ expect(myMock.called('/home')).to.be.true; ``` ## References to Request, Response, Headers, fetch and Promise -If you're using a non-global fetch implementation, or wish to use a custom Promise implementation, you may need to tell fetch-mock to use these when matching requests and returning responses. Do this by setting these properties on `fetchMock.config`, e.g + +If you're using a non-global fetch implementation, or wish to use a custom Promise implementation, you may need to tell fetch-mock to use these when matching requests and returning responses. Do this by setting these properties on `fetchMock.config`, e.g ``` const ponyfill = require('fetch-ponyfill')(); @@ -46,8 +50,9 @@ fetchMock.config = Object.assign(fetchMock.config, { Request: ponyfill.Request, Response: ponyfill.Response, fetch: ponyfill -}, +}, ``` + This should be done before running any tests. Note that when using `node-fetch`, `fetch-mock` will use the instance you already have installed so there should be no need to set any of the above (apart from `fetch`, which is required if you intend to use the `.spy()` method) diff --git a/docs/quickstart.md b/docs/quickstart.md index 26c6cb10..e4e51ed3 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -12,19 +12,22 @@ The commonest use case is `fetchMock.mock(matcher, response)`, where `matcher` is a string or regex to match and `response` is a statusCode, string or object literal. You can also use `fetchMock.once(url ...)` to limit to a single call or `fetchMock.get()`, `fetchMock.post()` etc. to limit to a method. All these methods are chainable so you can easily define several mocks in a single test. ## Analysing calls to your mock + `fetchMock.called(matcher)` reports if any calls matched your mock (or leave `matcher` out if you just want to check `fetch` was called at all). `fetchMock.lastCall()`, `fetchMock.lastUrl()` or `fetchMock.lastOptions()` give you access to the parameters last passed in to `fetch`. `fetchMock.done()` will tell you if `fetch` was called the expected number of times. ## Tearing down your mock + `fetchMock.reset()` resets the call history. `fetchMock.restore()` will also restore `fetch()` to its native implementation ## Example + Example with node: suppose we have a file `make-request.js` with a function that calls `fetch`: ```js module.exports = function makeRequest() { - return fetch("http://httpbin.org/get").then(function(response) { - return response.json(); - }); + return fetch('http://httpbin.org/get').then(function(response) { + return response.json(); + }); }; ``` @@ -36,10 +39,10 @@ var makeRequest = require('./make-request'); // Mock the fetch() global to always return the same value for GET // requests to all URLs. -fetchMock.get('*', {hello: 'world'}); +fetchMock.get('*', { hello: 'world' }); makeRequest().then(function(data) { - console.log('got data', data); + console.log('got data', data); }); // Unmock. @@ -50,5 +53,5 @@ Result: ```bash $ node mocked.js -'got data' { hello: 'world' } +'got data' { hello: 'world' } ``` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 1051c4e4..982aaf8e 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -10,6 +10,7 @@ ### `fetch` is assigned to a local variable, not a global First of all, consider whether you could just use `fetch` as a global. Here are 3 reasons why this is a good idea: + - The `fetch` standard defines it as a global (and in some cases it won't work unless bound to `window`), so to write isomorphic code it's probably best to stick to this pattern - [`isomorphic-fetch`](https://www.npmjs.com/package/isomorphic-fetch) takes care of installing it as a global in nodejs or the browser, so there's no effort on your part to do so. - `fetch-mock` is primarily designed to work with `fetch` as a global and your experience of using it will be far more straightforward if you follow this pattern @@ -19,19 +20,21 @@ Still not convinced? In that case `fetchMock.sandbox()` can be used to generate a function which you can pass in to a mock loading library such as [`mockery`](https://www.npmjs.com/package/mockery) instead of `fetch` ### `fetch` doesn't seem to be getting mocked? -* If using a mock loading library such as `mockery`, are you requiring the module you're testing after registering `fetch-mock` with the mock loader? You probably should be ([Example incorrect usage](https://github.com/wheresrhys/fetch-mock/issues/70)). If you're using ES6 `import` it may not be possible to do this without reverting to using `require()` sometimes. -* If using `isomorphic-fetch` in your source, are you assigning it to a `fetch` variable? You *shouldn't* be i.e. - * `import 'isomorphic-fetch'`, not `import fetch from 'isomorphic-fetch'` - * `require('isomorphic-fetch')`, not `const fetch = require('isomorphic-fetch')` + +- If using a mock loading library such as `mockery`, are you requiring the module you're testing after registering `fetch-mock` with the mock loader? You probably should be ([Example incorrect usage](https://github.com/wheresrhys/fetch-mock/issues/70)). If you're using ES6 `import` it may not be possible to do this without reverting to using `require()` sometimes. +- If using `isomorphic-fetch` in your source, are you assigning it to a `fetch` variable? You _shouldn't_ be i.e. + - `import 'isomorphic-fetch'`, not `import fetch from 'isomorphic-fetch'` + - `require('isomorphic-fetch')`, not `const fetch = require('isomorphic-fetch')` ### Environment doesn't support requiring fetch-mock? -* If your client-side code or tests do not use a loader that respects the browser field of package.json use `require('fetch-mock/es5/client')`. -* If you need to use fetch-mock without commonjs, you can include the precompiled `node_modules/fetch-mock/es5/client-browserified.js` in a script tag. This loads fetch-mock into the `fetchMock` global variable. -* For server side tests running in nodejs 0.12 or lower use `require('fetch-mock/es5/server')` + +- If your client-side code or tests do not use a loader that respects the browser field of package.json use `require('fetch-mock/es5/client')`. +- If you need to use fetch-mock without commonjs, you can include the precompiled `node_modules/fetch-mock/es5/client-browserified.js` in a script tag. This loads fetch-mock into the `fetchMock` global variable. +- For server side tests running in nodejs 0.12 or lower use `require('fetch-mock/es5/server')` ### Matching `Request` objects in node fails -In node, if using npm at a version less than 2 the `Request` constructor used by `fetch-mock` won't necessarily be the same as the one used by `node-fetch`. To fix this either: -* upgrade to npm@3 -* use `fetchMock.config.Request = myRequest`, where `myRequest` is a reference to the Request constructor used in your application code. +In node, if using npm at a version less than 2 the `Request` constructor used by `fetch-mock` won't necessarily be the same as the one used by `node-fetch`. To fix this either: +- upgrade to npm@3 +- use `fetchMock.config.Request = myRequest`, where `myRequest` is a reference to the Request constructor used in your application code. diff --git a/docs/v5-v6-upgrade.md b/docs/v5-v6-upgrade.md index 18354a90..32471a1f 100644 --- a/docs/v5-v6-upgrade.md +++ b/docs/v5-v6-upgrade.md @@ -7,28 +7,32 @@ For the most common use cases, and the basic mocking and inspecting api, very li # Changes ## Configuration and installation + - Requires `async / await` - for older node versions use v5, or require the transpiled version: `require('fetch-mock/es5/server')` -- *No change to mocking fetch as a global* +- _No change to mocking fetch as a global_ - Uses whatever version of `node-fetch` you have already installed in your project i.e. can be used with `node-fetch` 2 - `.sandbox()` no longer accepts a custom `Promise` implementation as a parameter - Sandboxes can now be 'sub-classed' i.e. define some mocks on a sandbox, then call `.sandbox()` on it to create a new one inheriting all its settings -- No longer has a `setImplementations()` method. Instead, assign custom `Promise` and `fetch` helper classes directly to the `config` property of the fetch-mock instance. This may be done before or after sandboxing +- No longer has a `setImplementations()` method. Instead, assign custom `Promise` and `fetch` helper classes directly to the `config` property of the fetch-mock instance. This may be done before or after sandboxing - No longer has a `configure()` method. Instead set options directly on the `.config` property. See the api docs for a complete list of options ## Routing + - `^` is no longer a valid way of matching the beginning of strings; use `begin:` instead - `overwriteRoutes` option allows for existing routes in a mock to be overwritten. It's also possible to define multiple routes with 'the same' matcher. Default behaviour is to error. Set to `false` to use old "buffer-like" behavior ## Responses + - `Content-Length` header generated by default for all responses (can be configured to not do so globally or per response.) - Objects will be converted to `JSON` responses if they contain any properties not listed in the docs as special response config options (previous behaviour was to convert if any of the properties were present). This means that e.g. `{user: 'me', status: 'happy'}` can easily be sent as `JSON` - `__redirectUrl` option is now named `redirectUrl` - `times` option is now named `repeat` ## Inspecting + - All inspection methods now filter, in addition to the existing check against `matcher.toString()`, based on the following rules - + An exact url - + `true` for matched calls only - + `false` for unmatched calls only - + `undefined` (or no argument) includes all calls to `fetch` in order in a single array, i.e. no longer an object with `matched` and `unmatched` properties + - An exact url + - `true` for matched calls only + - `false` for unmatched calls only + - `undefined` (or no argument) includes all calls to `fetch` in order in a single array, i.e. no longer an object with `matched` and `unmatched` properties - When `fetch` was last called with a `Request` object, `lastUrl()` and `lastOpts()` give easier access to the `url` and full `Request` object diff --git a/docs/v6-v7-upgrade.md b/docs/v6-v7-upgrade.md index a95ccebe..b0984325 100644 --- a/docs/v6-v7-upgrade.md +++ b/docs/v6-v7-upgrade.md @@ -3,22 +3,28 @@ # Changes ## `throws` option now rejects a Promise + A regression was introduced in v6 whereby the `throws: true` option woudl throw an uncaught error. The `fetch` api catches all its internal errors and returns a rejected `Promise` in every case, so this change has been reverted to be more useful for mocking typical `fetch` errors. ## Responses are wrapped in an ES Proxy + This is to enable a more powerful `flush()` method, able to wait for asynchronous resolution of response methods such as `.json()` and `.text()`. `flush(true)` will resolve only when Promises returnes by any response methods called before `flush()` have resolved ## Supports resolving dots in urls + As resolving `../` and `./` as relative paths is [speced url behaviour](https://url.spec.whatwg.org/#double-dot-path-segment), fetch-mock has been updated to also do this resolution when matching urls. URLs are normalised _before_ any matchers try to match against them as, to the `fetch` api, `http://thing/decoy/../quarry` is indistinguishable from `http://thing/quarry`, so it would make no sense to allow different mocking based on which variant is used. ## Agnostic as to whether hosts have a trailing slash or not + A side-effect of the above normalisation - using [whatwg-url](https://www.npmjs.com/package/whatwg-url) - is that fetch-mock is no longer able to distinguish between pathless URLs which do/do not end in a trailing slash i.e. `http://thing` behaves exactly the same as `http://thing/` when used in any of the library's APIs, and any mocks that match one will match the other. As mentioned above, URL normalization happens _before_ any matching is attempted. ## Request instances are normalized early to [url, options] pairs + The `fetch` api can be called with either a `Request` object or a `url` with an `options` object. To make the library easier to maintain and its APIs - in particular the call inspecting APIs such as `called()` - agnostic as to how `fetch` was called, Request objects are normalised early into `url`, `options` pairs. So the fetch args returned by `calls()` will always be of the form `[url, options]` even if `fetch` was called with a `Request` object. The original `Request` object is still provided as a `request` property on the `[url, opts]` array in case it is needed. ## Exporting as property -`fetch-mock` now has a property `fetchMock`, which means the libarry can be imported using any of the below + +`fetch-mock` now has a property `fetchMock`, which means the libary can be imported using any of the below ```js const fetchMock = require('fetch-mock'); @@ -29,6 +35,7 @@ const { fetchMock } = require('fetch-mock'); The reason for this should become clear in the next point ## Adds MATCHED and UNMATCHED constants + The inspection APIs e.g. `calls()` can be passed `true` or `false` to return matched/unmatched calls respectively. To aid with more comprehensible code, fetchMock now exports `MATCHED` and `UNMATCHED` constants, equal to `true` and `false`. Using `true` or `false` still works, but I'd recommend using the constants. Compare the readbility of the following: ```js @@ -39,29 +46,35 @@ fetchMock.called(MATCHED); ``` ## Able to match requests based on the path of the url -`fetchMock.mock('path:/apples/pears')` Will match any url whose `path` part is `apples/pears` + +`fetchMock.mock('path:/apples/pears')` Will match any url whose `path` part is `/apples/pears` ## More powerful inspection filtering + Previously, any filter passed in to `calls(filter)` etc... would always be converted to a string and then be used to lookup whether any fetch calls had been handled by a route matcher that also had the same `toString()` value. This is still the case, but if no calls match, then the filter will be converted into an on the fly route matcher, which is then used to filter the list of calls that were handled by fetch. This measn e.g. you can use any regex or glob to filter the calls. ### Example + ```js -fetchMock.mock('*', 200) -await fetch('/main-course/lasagne', {method: 'POST', headers: {discount: true}}); +fetchMock.mock('*', 200); +await fetch('/main-course/lasagne', { + method: 'POST', + headers: { discount: true } +}); await fetch('/main-course/bolognaise'); -fetchMock.called(/l.+gne/) // true -fetchMock.called('glob:/*/lasagne', {method: 'post'}) // true -fetchMock.called((url, options) => options.headers.discount) // true +fetchMock.called(/l.+gne/); // true +fetchMock.called('glob:/*/lasagne', { method: 'post' }); // true +fetchMock.called((url, options) => options.headers.discount); // true ``` -And, copied directly from the API docs: - Most of the methods below accept two parameters, `(filter, options)` - - `filter` Enables filtering fetch calls for the most commonly use cases. The behaviour can be counterintuitive. The following rules, applied in the order they are described, are used to try to retrieve calls. If any rule retrieves no calls the next rule will be tried. - - If `filter` is `undefined` all calls, matched _and_ unmatched, are returned - - If `filter` is `true` (or `fetchMock.MATCHED`) all calls that matched some route are returned - - If `filter` is `false` (or `fetchMock.UNMATCHED`) all calls that did not match any route are returned (when `.spy()`, `catch()` or `config.fallThroughToNetwork` were used to prevent errors being thrown) - - If `filter` is the name of a named route, all calls handled by that route are returned - - If `filter` is equal to `matcher` or `matcher.toString()` for a route, all calls handled by that route are returned - - `filter` is executed using the same execution plan as matchers used in `.mock()`. Any calls matched by it will be returned. If `options` is also passed this is used in a similar way to the options used by `mock()`. Alternatively, `options` can be a string specifying a `method` to filter by +And, copied directly from the API docs: + Most of the methods below accept two parameters, `(filter, options)` + - `filter` Enables filtering fetch calls for the most commonly use cases. The behaviour can be counterintuitive. The following rules, applied in the order they are described, are used to try to retrieve calls. If any rule retrieves no calls the next rule will be tried. + - If `filter` is `undefined` all calls, matched _and_ unmatched, are returned + - If `filter` is `true` (or `fetchMock.MATCHED`) all calls that matched some route are returned + - If `filter` is `false` (or `fetchMock.UNMATCHED`) all calls that did not match any route are returned (when `.spy()`, `catch()` or `config.fallThroughToNetwork` were used to prevent errors being thrown) + - If `filter` is the name of a named route, all calls handled by that route are returned + - If `filter` is equal to `matcher` or `matcher.toString()` for a route, all calls handled by that route are returned + - `filter` is executed using the same execution plan as matchers used in `.mock()`. Any calls matched by it will be returned. If `options` is also passed this is used in a similar way to the options used by `mock()`. Alternatively, `options` can be a string specifying a `method` to filter by