Skip to content

Commit

Permalink
Merge pull request #350 from wheresrhys/revert-348-revert-346-advance…
Browse files Browse the repository at this point in the history
…d-filters

Revert "Revert "Advanced filters""
  • Loading branch information
wheresrhys committed Aug 20, 2018
2 parents cb44374 + ac8e5b0 commit 315bf4b
Show file tree
Hide file tree
Showing 25 changed files with 4,506 additions and 3,644 deletions.
1 change: 0 additions & 1 deletion .eslintcache

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ npm-debug.log
coverage/
/test/fixtures/built-sw.js
.nyc_output
.eslintcache
26 changes: 14 additions & 12 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ Replaces `fetch` with a stub which records its calls, grouped by route, and opti
* `*` 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
* `path:/posts/2018/7/3` to match urls with a given path
* `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)

_Note that if using `end:` or an exact url matcher, `fetch-mock` ([for good reason](https://url.spec.whatwg.org/#url-equivalence)) is unable to distinguish whether URLs without a path end in a trailing slash or not i.e. `http://thing` is treated the same as `http://thing/`_
* `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
Expand All @@ -33,7 +35,7 @@ Replaces `fetch` with a stub which records its calls, grouped by route, and opti
* `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
* `throws`: If this property is present then fetch returns a `Promise` rejected with the value of `throws`
* `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)
Expand Down Expand Up @@ -92,15 +94,15 @@ Chainable method that clears all data recorded for `fetch()`'s calls. *It will n
## 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
- `method` A http method to filter by
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


#### `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.
Expand All @@ -118,10 +120,10 @@ Returns the arguments for the last matched call to fetch
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
Returns the options for the last matched call to fetch. When `fetch` was last called using a `Request` instance, a set of `options` inferred from the `Request` 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.
Returns a `Promise` that resolves once all fetches handled by fetch-mock have resolved. Pass in `true` to wait for all response methods (`res.json()`, `res.text()`, etc.) to resolve too. Useful for testing code that uses `fetch` but doesn't return a promise.

## Config

Expand Down
22 changes: 17 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
- [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:
In most environments use one of the following to use it in your code.

```js
const fetchMock = require('fetch-mock');

// The following is recommended in order to expose constants that
// will make tests that check for matched or unmatched calls more
// readable
const { fetchMock, MATCHED, UNMATCHED } = require('fetch-mock');
```


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`

## 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
Expand All @@ -36,7 +48,7 @@ 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')();
Expand All @@ -46,7 +58,7 @@ fetchMock.config = Object.assign(fetchMock.config, {
Request: ponyfill.Request,
Response: ponyfill.Response,
fetch: ponyfill
},
},
```
This should be done before running any tests.

Expand Down
69 changes: 69 additions & 0 deletions docs/v6-v7-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Upgrading from V6 to V7

Most changes are relatively minor and shouldn't affect most users.

# 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 third item in the args 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

```js
const fetchMock = require('fetch-mock');
const fetchMock = require('fetch-mock').fetchMock;
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
const { fetchMock, MATCHED, UNMATCHED } = require('fetch-mock');

fetchMock.called(true);
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`

## 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}});
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
```
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

2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = karma =>
},
webpack: {
mode: 'development',
devtool: 'source-map',
devtool: 'inline-source-map',
module: {
rules: [
{
Expand Down
Loading

0 comments on commit 315bf4b

Please sign in to comment.