Skip to content

Commit

Permalink
Merge pull request #11 from wheresrhys/debug
Browse files Browse the repository at this point in the history
added debug info
  • Loading branch information
wheresrhys committed May 10, 2015
2 parents 31ea6fe + 2af85fd commit a227aea
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ Mock http requests made using fetch (or isomorphic-fetch)
*notes*
- When using isomorphic-fetch or node-fetch `fetch` should be added as a global
- fetch-mock doesn't declare `fetch` or `Promise` as dependencies; as you're testing `fetch` it's assumed you're already taking care of these globals
- fetch-mock uses [npm debug](https://www.npmjs.com/package/debug). To output useful messages for debugging set the environment variable `DEBUG=fetch-mock`
- If you prefer documentation by example skip to the bottom of this README


## API

`require('fetch-mock')` exports a singleton with the following methods
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"chai": "^2.3.0",
"coveralls": "^2.11.2",
"debowerify": "^1.2.1",
"debug": "^2.2.0",
"es6-promise": "^2.1.1",
"karma": "^0.12.31",
"karma-browserify": "^4.1.2",
Expand Down
76 changes: 63 additions & 13 deletions src/fetch-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,40 @@ var Response;
var stream;
var Blob;
var theGlobal;

var debug = require('debug')('fetch-mock')

function mockResponse (url, config) {
debug('mocking response for ' + url);
// allow just body to be passed in as this is the commonest use case
if (typeof config === 'number') {
debug('status response detected for ' + url);
config = {
status: config
};
} else if (typeof config === 'string' || !(config.body || config.headers || config.throws || config.status)) {
debug('body response detected for ' + url);
config = {
body: config
};
} else {
debug('full config response detected for ' + url);
}

if (config.throws) {
debug('mocking failed request for ' + url);
return Promise.reject(config.throws);
}
var opts = config.opts || {};
opts.url = url;
opts.status = config.status || 200;
opts.headers = config.headers ? new Headers(config.headers) : new Headers();


var body = config.body;
if (config.body != null && typeof body === 'object') {
body = JSON.stringify(body);
}

debug('sending body "' + body + '"" for ' + url);
if (stream) {
var s = new stream.Readable();
if (body != null) {
Expand All @@ -45,6 +52,9 @@ function mockResponse (url, config) {
}

function compileRoute (route) {

debug('compiling route: ' + route.name);

if (!route.name) {
throw 'each route must be named';
}
Expand All @@ -60,16 +70,19 @@ function compileRoute (route) {
if (typeof route.matcher === 'string') {
var expectedUrl = route.matcher;
if (route.matcher.indexOf('^') === 0) {
debug('constructing starts with string matcher for route: ' + route.name);
expectedUrl = expectedUrl.substr(1);
route.matcher = function (url) {
return url.indexOf(expectedUrl) === 0;
};
} else {
debug('constructing string matcher for route: ' + route.name);
route.matcher = function (url) {
return url === expectedUrl;
};
}
} else if (route.matcher instanceof RegExp) {
debug('constructing regex matcher for route: ' + route.name);
var urlRX = route.matcher;
route.matcher = function (url) {
return urlRX.test(url);
Expand All @@ -89,6 +102,7 @@ var FetchMock = function (opts) {
};

FetchMock.prototype.registerRoute = function (name, matcher, response) {
debug('registering routes');
var routes;
if (name instanceof Array) {
routes = name;
Expand All @@ -101,30 +115,40 @@ FetchMock.prototype.registerRoute = function (name, matcher, response) {
} else {
routes = [name];
}

debug('registering routes: ' + routes.map(function (r) {return r.name}));

this.routes = this.routes.concat(routes.map(compileRoute));
};

FetchMock.prototype.unregisterRoute = function (name) {
var names;
if (!name) {
FetchMock.prototype.unregisterRoute = function (names) {

if (!names) {
debug('unregistering all routes');
this.routes = [];
return;
}
if (name instanceof Array) {
names = name;
} else {
names = [name];
if (!(names instanceof Array)) {
names = [names];
}

debug('unregistering routes: ' + names);

this.routes = this.routes.filter(function (route) {
return names.indexOf(route.name) === -1;
var keep = names.indexOf(route.name) === -1;
if (!keep) {
debug('unregistering route ' + route.name);
}
return keep;
});
};

FetchMock.prototype.getRouter = function (config) {
debug('building router');
var routes;

if (config.routes) {
debug('applying one time only routes');
if (!(config.routes instanceof Array)) {
config.routes = [config.routes];
}
Expand All @@ -135,12 +159,15 @@ FetchMock.prototype.getRouter = function (config) {
});
routes = config.routes.map(function (route) {
if (typeof route === 'string') {
debug('applying preregistered route ' + route);
return preRegisteredRoutes[route];
} else {
debug('applying one time route ' + route.name);
return compileRoute(route);
}
});
} else {
debug('no one time only routes defined. Using preregistered routes only');
routes = this.routes;
}

Expand All @@ -157,18 +184,30 @@ FetchMock.prototype.getRouter = function (config) {

return function (url, opts) {
var response;

debug('searching for matching route for ' + url);
routes.some(function (route) {

if (route.matcher(url, opts)) {
debug('Found matching route (' + route.name + ') for ' + url);
this.push(route.name, [url, opts]);
response = config.responses[route.name] || route.response;

if (config.responses[route.name]) {
debug('Overriding response for ' + route.name);
response = config.responses[route.name];
} else {
debug('Using default response for ' + route.name);
response = route.response;
}

if (typeof response === 'function') {
debug('Constructing dynamic response for ' + route.name);
response = response(url, opts);
}
return true;
}
}.bind(this));

debug('returning response for ' + url);
return response;
}.bind(this);
};
Expand All @@ -179,9 +218,10 @@ FetchMock.prototype.push = function (name, call) {
};

FetchMock.prototype.mock = function (config) {
debug('mocking fetch');
var self = this;
if (this.isMocking) {
throw 'fetch-mock is already mocking routes. Call .restore() before mocking again';
throw 'fetch-mock is already mocking routes. Call .restore() before mocking again or use .reMock() if this is intentional';
}

this.isMocking = true;
Expand All @@ -190,27 +230,35 @@ FetchMock.prototype.mock = function (config) {
var router = this.getRouter(config);
config.greed = config.greed || 'none';

debug('applying sinon.stub to fetch')
sinon.stub(theGlobal, 'fetch', function (url, opts) {
var response = router(url, opts);
if (response) {
debug('response found for ' + url);
return mockResponse(url, response);
} else {
debug('response not found for ' + url);
self.push('__unmatched', [url, opts]);
if (config.greed === 'good') {
debug('sending default good response');
return mockResponse(url, {body: 'unmocked url: ' + url});
} else if (config.greed === 'bad') {
debug('sending default bad response');
return mockResponse(url, {throws: 'unmocked url: ' + url});
} else {
debug('forwarding to default fetch');
return defaultFetch(url, opts);
}
}
});
};

FetchMock.prototype.restore = function () {
debug('restoring fetch');
this.isMocking = false;
this.reset();
theGlobal.fetch.restore();
debug('fetch restored');
};

FetchMock.prototype.reMock = function (config) {
Expand All @@ -219,8 +267,10 @@ FetchMock.prototype.reMock = function (config) {
};

FetchMock.prototype.reset = function () {
debug('resetting call logs');
this._calls = {};
theGlobal.fetch.reset();
debug('call logs reset');
};

FetchMock.prototype.calls = function (name) {
Expand Down
3 changes: 2 additions & 1 deletion test/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ module.exports = function (fetchMock, theGlobal) {

it('call fetch if no routes defined', function () {
fetchMock.mock();
fetch();
fetch('url', {prop: 'val'});
expect(fetchCalls.length).to.equal(1);
expect(fetchCalls[0]).to.eql(['url', {prop: 'val'}]);
fetchMock.restore();
});

Expand Down

0 comments on commit a227aea

Please sign in to comment.