diff --git a/index.js b/index.js index 7daea87..23425f6 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,6 @@ */ var pathtoRegexp = require('path-to-regexp'); - /** * Expose public API */ @@ -14,6 +13,7 @@ mock.get = defineRoute.bind(null, 'GET'); mock.post = defineRoute.bind(null, 'POST'); mock.put = defineRoute.bind(null, 'PUT'); mock.del = defineRoute.bind(null, 'DELETE'); +mock.patch = defineRoute.bind(null, 'PATCH'); /** * Request timeout @@ -26,12 +26,39 @@ mock.timeout = 0; */ var routes = []; +/** + * Original superagent methods + * @type {{}} + */ +var originalMethods = {}; + /** * Unregister all routes */ mock.clearRoutes = function() { routes.splice(0, routes.length) -} +}; + +/** + * Map api method to http method + */ +var methodsMapping = { + get: 'GET', + post: 'POST', + put: 'PUT', + del: 'DELETE', + patch: 'PATCH' +}; + +/** + * Unregister specific route + */ +mock.clearRoute = function(method, url) { + method = methodsMapping[method] || method; + routes = routes.filter(function(route) { + return !(route.url === url && route.method === method); + }); +}; /** * Mock @@ -51,16 +78,18 @@ function mock(superagent) { } }; - // Patch superagent - patch(superagent, 'get', 'GET', state); - patch(superagent, 'post', 'POST', state); - patch(superagent, 'put', 'PUT', state); - patch(superagent, 'del', 'DELETE', state); + // patch api methods (http) + for (var method in methodsMapping) { + if (methodsMapping.hasOwnProperty(method)) { + var httpMethod = methodsMapping[method]; + patch(superagent, method, httpMethod, state); + } + } var reqProto = superagent.Request.prototype; // Patch Request.end() - var oldEnd = superagent.Request.prototype.end; + var oldEnd = originalMethods.end = superagent.Request.prototype.end; reqProto.end = function(cb) { var current = state.current; if (current) { @@ -77,10 +106,10 @@ function mock(superagent) { }; // Patch Request.set() - var oldSet = reqProto.set; + var oldSet = originalMethods.set = reqProto.set; reqProto.set = function(key, val) { if (!state.current) { - return oldSet(key, val); + return oldSet.call(this, key, val); } // Recursively set keys if passed an object if (isObject(key)) { @@ -94,22 +123,36 @@ function mock(superagent) { } state.request.headers[key.toLowerCase()] = val; return this; - } + }; // Patch Request.send() - var oldSend = reqProto.send + var oldSend = originalMethods.send = reqProto.send; reqProto.send = function(data) { if (!state.current) { - return oldSend(data); + return oldSend.call(this, data); } state.request.body = mergeObjects(state.current.body, data); return this; - } + }; return mock; // chaining } +mock.unmock = function(superagent) { + ['get', 'post', 'put', 'patch', 'del'].forEach(function(method) { + superagent[method] = originalMethods[method]; + }); + + var reqProto = superagent.Request.prototype; + + ['end', 'set', 'send'].forEach(function(method) { + reqProto[method] = originalMethods[method]; + }); + + delete superagent._patchedBySuperagentMocker; +}; + /** * find route that matched with url and method * TODO: Remove data @@ -137,7 +180,7 @@ function defineRoute(method, url, handler) { * Patch superagent method */ function patch(superagent, prop, method, state) { - var old = superagent[prop]; + var old = originalMethods[prop] = superagent[prop]; superagent[prop] = function (url, data, fn) { state.current = match(method, url, data); state.request = { diff --git a/readme.md b/readme.md index c7041a7..d065043 100644 --- a/readme.md +++ b/readme.md @@ -92,7 +92,7 @@ request ; ``` -`mock.put()` method works in a similar way. +`mock.put()`, `mock.patch()` methods works in a similar way. ### Teardown @@ -124,6 +124,24 @@ define('My API module', function(){ }) ``` +Or you can remove only one specified route (by method and url) + +```js +// to register route +mock.get('/me', function(){done()}) + +... + +// to remove registered handler +mock.clearRoute('get', '/me'); + +``` + +### Rollback library effect + +In some cases it will be useful to remove patches from superagent lib after using mocks. +In this cases you can use ```mock.unmock()``` method, that will rollback all patches that ```mock(superagent)``` call make. + ## License MIT © [Shuvalov Anton](http://shuvalov.info) diff --git a/test.js b/test.js index f47c3bc..a4f52ba 100644 --- a/test.js +++ b/test.js @@ -11,7 +11,6 @@ var mock = process.env.SM_COV ? require('./index-cov')(request) : require('./index')(request); - describe('superagent mock', function() { beforeEach(function() { @@ -63,6 +62,20 @@ describe('superagent mock', function() { ; }); + it('should mock for patch', function(done) { + mock.patch('/topics/:id', function(req) { + return { id: req.params.id, content: req.body.content }; + }); + request + .patch('/topics/7', { id: 7, content: 'hello world, bitch!11' }) + .end(function(_, data) { + data.should.have.property('id', '7'); + data.should.have.property('content', 'hello world, bitch!11'); + done(); + }) + ; + }); + it('should mock for delete', function(done) { mock.del('/topics/:id', function(req) { return { id: req.params.id, content: req.body.content }; @@ -139,6 +152,29 @@ describe('superagent mock', function() { }); }); + it('should clear registered specific route', function(done) { + mock + .get('/topics', noop) + .get('/posters', function() { + return { id: 7 }; + }); + mock.clearRoute('get', '/topics'); + request + .get('/topics') + .end(function(err, res) { + should.throws(function() { + should.ifError(err); + }, /ECONNREFUSED/); + + request + .get('/posters') + .end(function(_, data) { + data.should.have.property('id', 7); + done(); + }); + }); + }); + it('should provide error when method throws', function(done) { var error = Error('This should be in the callback!'); mock.get('http://example.com', function(req) { @@ -217,7 +253,7 @@ describe('superagent mock', function() { return req.body; }); request - .post('/topics/5', { content: 'Hello Universe'}) + .post('/topics/5', { content: 'Hello Universe' }) .send({ content: 'Hello world', title: 'Yay!' }) .end(function(_, data) { data.should.have.property('title', 'Yay!'); @@ -227,12 +263,15 @@ describe('superagent mock', function() { ; }); + it('should remove patches by unmock()', function() { + mock.unmock(request); + (request._patchedBySuperagentMocker === void 0).should.be.true; + }); + }); }); - - /** * Just noop */