Skip to content

Commit

Permalink
Merge pull request #365 from wheresrhys/name-method
Browse files Browse the repository at this point in the history
Name method
  • Loading branch information
wheresrhys authored Sep 23, 2018
2 parents bbdd390 + addc174 commit aff9b9d
Show file tree
Hide file tree
Showing 19 changed files with 527 additions and 279 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ coverage/*
es5/*
.nyc_output/
docs/js
docs/_site
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export PATH := $(PATH):./node_modules/.bin

.PHONY: test
.PHONY: test docs

test-dev:
karma start --browsers=Chrome
Expand Down Expand Up @@ -39,3 +39,6 @@ bundle:
--output-library fetchMock \
--entry ./es5/client.js \
--output-filename ./es5/client-bundle.js

docs:
cd docs; jekyll serve build --watch
24 changes: 21 additions & 3 deletions docs/_api-inspection/done.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
---
title: .done(filter, options)
title: .done(filter)
navTitle: .done()
position: 6
description: |-
Returns a Boolean indicating whether `fetch` was called the expected number of times (or has been called at least once if `repeat` is undefined for the route)
parameters:
- name: matcherOrName
content: Rule for matching calls to `fetch`.
options:
- types:
- undefined
- true
content: |-
Runs over all calls matched by `fetch`
- name: routeIdentifier
types:
- String|RegExp|function
content: |-
All routes have an identifier:
- If it's a named route, the identifier is the route's name
- If the route is unnamed, the identifier is the `matcher` passed in to `.mock()`
All calls that were handled by the route with the given identifier will be retrieved
content_markdown: |-
Unlike the other methods for inspecting calls, unmatched calls are irrelevant. If no `filter` is passed, `done()` returns `true` if every route has been called the number of expected times.
{: .warning}
If several routes have the same matcher/url, but use [mocking options](#apimockingmock_options), the recommended way to handle this is to name each route and filter using those names
{: .info}
---
70 changes: 51 additions & 19 deletions docs/_api-inspection/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,13 @@ description: |-
Most inspection methods take two arguments — `filter` and `options` — which allow groups of fetch calls to be extracted and inspected.
parameters:
- name: filter
types:
- String
- RegExp
- Function
content: |-
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.
Allows filtering of calls to fetch based on various criteria
options:
- name: matcher
content: |-
If `options` is defined (it can even be an empty object), `filter` will be executed using the same execution plan as [matchers](#api-mockingmock_matcher). Any calls matched by it will be returned.
types:
- String
- RegExp
- Function
- types:
- undefined
content: |-
Retrieves all calls made to `fetch`, whether fetch-mock matched them or not
Retrieves all calls made to `fetch`, whether a specific route matched them or not
- types:
- true
content: |-
Expand All @@ -31,17 +20,60 @@ parameters:
- false
content: |-
Retrieves all calls not matched by `fetch` (i.e. those handled by `catch()` or `spy()`. `fetchMock.UNMATCHED` is an alias for `false` and may be used to make tests more readable
- name: route
- name: routeIdentifier
types:
- String
content: Retrieves calls handled by a named route (see [mocking options](#api-mockingmock_options). Failing that, a route whose matcher, when coerced to a string, is equal to the string provided
- name: asdkash d
content: Do I want to fallback to a matcher again?? Seems confusing as hell
- String|RegExp|function
content: |-
All routes have an identifier:
- If it's a named route, the identifier is the route's name
- If the route is unnamed, the identifier is the `matcher` passed in to `.mock()`
All calls that were handled by the route with the given identifier will be retrieved
- name: matcher
types:
- String|RegExp|function
content: |-
Any matcher compatible with the [mocking api](#api-mockingmock_matcher) can be passed in to filter the calls arbitrarily
- name: options
types:
- Object
- String
content: |-
Either an object compatible with the [mocking api](#api-mockingmock_options) or a string specifying a http `method` to filter by
Either an object compatible with the [mocking api](#api-mockingmock_options) or a string specifying a http `method` to filter by. This will be used to filter the list of calls further
content_markdown: |-
If in doubt, [add a name to your route](#api-mockingmock_options), and pass in that name to retrieve exactly the calls you want.
{:.info}
Note that when matching calls handled by a route with a `RegExp` or `function` matcher, use the exact `RegExp`|`function` you used in your mock, e.g.
{:.warning}
```javascript
const matcherRX = /user\/biff/
fm.mock(matcherRX, 200)
...
fm.called(matcherRX)
```
not
```javascript
fm.mock(/user\/biff/, 200)
...
fm.called(/user\/biff/)
```
The second example _will_ retrieve the expected calls in simple test scenarios because if no routes match using the `identifier` the `RegExp` will be executed as a `RegExp` matcher. But in more complex scenarios where e.g. there are several routes handling similar paths, it might retrieve calls that were actually handled by different, similar route e.g.
```javascript
const matcherRX = /user\/biff/
fm
.mock('end:user/biff')
.mock(matcherRX, 200)
...
// this will retrieve calls handled by either route
fm.called(/user\/biff/)
// this will retrieve only calls handeld by the second route
fm.called(/user\/biff/)
```
---
1 change: 1 addition & 0 deletions docs/_usage/custom-classes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Custom subclasses
position: 6
parentMethod: installation
content_markdown: |-
Some fetch-mock internals require access to the `Request`, `Response` and `Headers` constructors provided by your chosen `fetch` implementation. These should be set on the `fetchMock.config` object
{: .warning}
Expand Down
1 change: 1 addition & 0 deletions docs/_usage/global-non-global.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Global or non-global
position: 3
parentMethod: installation
content_markdown: |-
`fetch` can be used by your code globally or locally. It's important to determine which one applies to your codebase as it will impact how you use `fetch-mock`
{: .warning}
Expand Down
1 change: 1 addition & 0 deletions docs/_usage/polyfilling.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Polyfilling fetch
position: 5
parentMethod: installation
content_markdown: |-
Many older browsers require polyfilling the `fetch` global. The following approaches can be used
Expand Down
23 changes: 23 additions & 0 deletions docs/v6-v7-upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@ fetchMock.called(MATCHED);

`fetchMock.mock('path:/apples/pears')` Will match any url whose `path` part is `/apples/pears`

## done(filter) no longer filterable by method
This added a lot of complexity to the source code. Users who were using this feature are encouraged to give names to routes handling different methods and filter using those names

e.g. before

```javascript
fetchMock
.getOnce('http://route/form', 200)
.postOnce('http://route/form', 201)

fetchMock.done('http://route/form', 'post')
```

after

```javascript
fetchMock
.getOnce('http://route/form', 200, {name: 'get-form'})
.postOnce('http://route/form', 201, {name: 'post-form'})

fetchMock.done('post-form')
```

## 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.
Expand Down
18 changes: 5 additions & 13 deletions src/lib/compile-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ const {
} = require('./request-utils');

const stringMatchers = {
begin: targetString => {
return url => url.indexOf(targetString) === 0;
},
end: targetString => {
return url => url.substr(-targetString.length) === targetString;
},
begin: targetString => url => url.indexOf(targetString) === 0,
end: targetString => url => url.substr(-targetString.length) === targetString,
glob: targetString => {
const urlRX = glob(targetString);
return url => urlRX.test(url);
Expand Down Expand Up @@ -99,8 +95,8 @@ const getUrlMatcher = route => {
// of the route to allow for e.g. http://it.at.there being indistinguishable
// from http://it.at.there/ once we start generating Request/Url objects
const expectedUrl = normalizeUrl(matcher);
if (route.__unnamed) {
route.name = expectedUrl;
if (route.identifier === matcher) {
route.identifier = expectedUrl;
}

return url => {
Expand All @@ -124,14 +120,10 @@ const sanitizeRoute = route => {
);
}

if (!route.name) {
route.name = route.matcher.toString();
route.__unnamed = true;
}

if (route.method) {
route.method = route.method.toLowerCase();
}
route.identifier = route.name || route.matcher;

return route;
};
Expand Down
22 changes: 11 additions & 11 deletions src/lib/fetch-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ FetchMock.executeRouter = function(url, options, request) {
console.warn(`Unmatched ${(options && options.method) || 'GET'} to ${url}`); // eslint-disable-line
}

this.push(null, { url, options, request });
this.push({ url, options, request, unmatched: true });

if (this.fallbackResponse) {
return { response: this.fallbackResponse };
Expand Down Expand Up @@ -133,7 +133,12 @@ FetchMock.router = function(url, options, request) {
const route = this.routes.find(route => route.matcher(url, options, request));

if (route) {
this.push(route.name, { url, options, request });
this.push({
url,
options,
request,
identifier: route.identifier
});
return route;
}
};
Expand All @@ -148,17 +153,12 @@ FetchMock.getNativeFetch = function() {
return func;
};

FetchMock.push = function(name, { url, options, request }) {
FetchMock.push = function({ url, options, request, unmatched, identifier }) {
const args = [url, options];
args.request = request;
if (name) {
this._calls[name] = this._calls[name] || [];
this._calls[name].push(args);
this._allCalls.push(args);
} else {
args.unmatched = true;
this._allCalls.push(args);
}
args.identifier = identifier;
args.unmatched = unmatched;
this._calls.push(args);
};

module.exports = FetchMock;
2 changes: 1 addition & 1 deletion src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ FetchMock.createInstance = function(isLibrary) {
instance.fallbackResponse = this.fallbackResponse || undefined;
instance.config = Object.assign({}, this.config || FetchMock.config);
instance._calls = {};
instance._allCalls = [];
instance._calls = [];
instance._holdingPromises = [];
instance.bindMethods();
if (isLibrary) {
Expand Down
Loading

0 comments on commit aff9b9d

Please sign in to comment.