UNPKG

fetch-mock

Version:

Mock http requests made using fetch (or isomorphic-fetch)

823 lines (750 loc) 23.2 kB
'use strict'; module.exports = function (fetchMock, theGlobal) { var fetchCalls = []; var expect = require('chai').expect; // we can't use sinon to spy on fetch in these tests as fetch-mock // uses it internally and sinon doesn't allow spying on a previously // stubbed function, so just use this very basic stub var dummyFetch = theGlobal.fetch = fetchMock.realFetch = function () { fetchCalls.push([].slice.call(arguments)); return Promise.resolve(arguments); }; var err = function (err) { console.log(error); } describe('fetch-mock', function () { afterEach(function () { fetchCalls = []; }); describe('default behaviour', function () { it('call fetch if no routes defined', function () { fetchMock.mock(); fetch('url', {prop: 'val'}); expect(fetchCalls.length).to.equal(1); expect(fetchCalls[0]).to.eql(['url', {prop: 'val'}]); fetchMock.restore(); }); it('restores fetch', function () { fetchMock.mock(); fetchMock.restore(); expect(fetch).to.equal(dummyFetch); }); it('throw() if attempting to mock more than once', function () { fetchMock.mock(); expect(function () { fetchMock.mock(); }).to.throw(); fetchMock.restore(); }); it('allow remocking after being restored', function () { fetchMock.mock(); fetchMock.restore(); expect(function () { fetchMock.mock(); fetchMock.restore(); }).not.to.throw(); }); }); describe('mocking fetch calls', function () { beforeEach(function () { try { fetchMock.restore(); } catch (e) {} }); describe('api parameters', function () { it('accepts a single route', function () { expect(function () { fetchMock.mock({ routes: {name: 'route', matcher: 'http://it.at.there', response: 'ok'} }); }).not.to.throw(); }); it('accepts an array of routes', function () { expect(function () { fetchMock.mock({ routes: [{name: 'route1', matcher: 'http://it.at.there', response: 'ok'}, {name: 'route2', matcher: 'http://it.at.there', response: 'ok'}] }); }).not.to.throw(); }); it('expects a name', function () { expect(function () { fetchMock.mock({ routes: {matcher: 'http://it.at.there', response: 'ok'} }); }).to.throw(); }); it('expects a matcher', function () { expect(function () { fetchMock.mock({ routes: {name: 'route', response: 'ok'} }); }).to.throw(); }); it('expects a response', function () { expect(function () { fetchMock.mock({ routes: {name: 'route', matcher: 'http://it.at.there'} }); }).to.throw(); }); it('expects unique route names', function () { expect(function () { fetchMock.mock({ routes: [{name: 'route', matcher: 'http://it.at.there', response: 'ok'}, {name: 'route', matcher: 'http://it.at.there', response: 'ok'}] }); }).to.throw(); }); }); describe('shorthand notation', function () { it('accepts matcher, method, route triples', function () { expect(function () { fetchMock.mock('http://it.at.there', 'PUT', 'ok'); }).not.to.throw(); fetch('http://it.at.there', {method: 'PUT'}); expect(fetchMock.calls().length).to.equal(1); }); it('accepts matcher, route pairs', function () { expect(function () { fetchMock.mock('http://it.at.there', 'ok'); }).not.to.throw(); fetch('http://it.at.there'); expect(fetchMock.calls().length).to.equal(1); }); it('accepts single route', function () { expect(function () { fetchMock.mock({name: 'route', matcher: 'http://it.at.there', response: 'ok'}); }).not.to.throw(); fetch('http://it.at.there'); expect(fetchMock.calls('route').length).to.equal(1); }); it('accepts array of routes', function () { expect(function () { fetchMock.mock([ {name: 'route1', matcher: 'http://it.at.there', response: 'ok'}, {name: 'route2', matcher: 'http://it.at.where', response: 'ok'} ]); }).not.to.throw(); fetch('http://it.at.there'); fetch('http://it.at.where'); expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(1); }); }); describe('unmatched routes', function () { it('record history of unmatched routes', function (done) { fetchMock.mock(); Promise.all([fetch('http://1', {method: 'GET'}), fetch('http://2', {method: 'POST'})]) .then(function () { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('__unmatched')).to.be.true; var unmatchedCalls = fetchMock.calls('__unmatched'); expect(unmatchedCalls.length).to.equal(2); expect(unmatchedCalls[0]).to.eql(['http://1', {method: 'GET'}]); expect(unmatchedCalls[1]).to.eql(['http://2', {method: 'POST'}]); done(); }); }); it('configure to send good responses', function (done) { fetchMock.mock({greed: 'good'}); fetch('http://1') .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('__unmatched')).to.be.true; expect(fetchMock.calls('__unmatched').length).to.equal(1); expect(res.status).to.equal(200); res.text().then(function (text) { expect(text).to.equal('unmocked url: http://1'); done(); }); }); }); it('configure to send bad responses', function (done) { fetchMock.mock({greed: 'bad'}); fetch('http://1') .catch(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('__unmatched')).to.be.true; expect(res).to.equal('unmocked url: http://1'); done(); }); }); it('configure to pass through to native fetch', function (done) { fetchMock.mock({greed: 'none'}); fetch('http://1') .then(function () { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('__unmatched')).to.be.true; expect(fetchCalls.length).to.equal(1); expect(fetchCalls[0].length).to.equal(2); done(); }) }); }); describe('route matching', function () { it('match exact strings', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: 'ok' } }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.thereabouts')]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route')).to.be.true; expect(fetchMock.calls('route').length).to.equal(1); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }); }); it('match strings starting with a string', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: '^http://it.at.there', response: 'ok' } }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.thereabouts'), fetch('http://it.at.hereabouts')]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route')).to.be.true; expect(fetchMock.calls('route').length).to.equal(2); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }); }); it('match regular expressions', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: /http\:\/\/it\.at\.there\/\d+/, response: 'ok' } }); Promise.all([fetch('http://it.at.there/'), fetch('http://it.at.there/12345'), fetch('http://it.at.there/abcde')]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route')).to.be.true; expect(fetchMock.calls('route').length).to.equal(1); expect(fetchMock.calls('__unmatched').length).to.equal(2); done(); }); }); it('match using custom functions', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: function (url, opts) { return url.indexOf('logged-in') > -1 && opts && opts.headers && opts.headers.authorized === true; }, response: 'ok' } }); Promise.all([ fetch('http://it.at.there/logged-in', {headers:{authorized: true}}), fetch('http://it.at.there/12345', {headers:{authorized: true}}), fetch('http://it.at.there/logged-in') ]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route')).to.be.true; expect(fetchMock.calls('route').length).to.equal(1); expect(fetchMock.calls('__unmatched').length).to.equal(2); done(); }); }); it('match method', function(done) { fetchMock.mock({ routes: [{ name: 'route1', method: 'get', matcher: 'http://it.at.here', response: 'ok' }, { name: 'route2', method: 'put', matcher: 'http://it.at.here', response: 'ok' }] }); Promise.all([fetch('http://it.at.here', {method: 'put'}), fetch('http://it.at.here'), fetch('http://it.at.here', {method: 'GET'}), fetch('http://it.at.here', {method: 'delete'})]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route1')).to.be.true; expect(fetchMock.called('route2')).to.be.true; expect(fetchMock.calls('route1').length).to.equal(2); expect(fetchMock.calls('route2').length).to.equal(1); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }).catch(done); }); it('match multiple routes', function (done) { fetchMock.mock({ routes: [{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: 'http://it.at.here', response: 'ok' }] }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here'), fetch('http://it.at.nowhere')]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route1')).to.be.true; expect(fetchMock.called('route2')).to.be.true; expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(1); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }); }); it('match first compatible route when many routes match', function (done) { fetchMock.mock({ routes: [{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: '^http://it.at.there', response: 'ok' }] }); Promise.all([fetch('http://it.at.there')]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route1')).to.be.true; expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(0); done(); }); }); it('record history of calls to matched routes', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: '^http://it.at.there', response: 'ok' } }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.thereabouts', {headers: {head: 'val'}})]) .then(function (res) { expect(fetchMock.called()).to.be.true; expect(fetchMock.called('route')).to.be.true; expect(fetchMock.calls().route).to.exist; expect(fetchMock.calls('route')[0]).to.eql(['http://it.at.there', undefined]); expect(fetchMock.calls('route')[1]).to.eql(['http://it.at.thereabouts', {headers: {head: 'val'}}]); done(); }); }); it('be possible to reset call history', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: '^http://it.at.there', response: 'ok' } }); fetch('http://it.at.there') .then(function (res) { fetchMock.reset(); expect(fetchMock.called()).to.be.false; expect(fetchMock.called('route')).to.be.false; expect(fetchMock.calls('route').length).to.equal(0); done(); }); }); it('restoring clears call history', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: '^http://it.at.there', response: 'ok' } }); fetch('http://it.at.there') .then(function (res) { fetchMock.restore(); expect(fetchMock.called()).to.be.false; expect(fetchMock.called('route')).to.be.false; expect(fetchMock.calls('route').length).to.equal(0); done(); }); }); }); describe('responses', function () { it('respond with a status', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: 300 } }); fetch('http://it.at.there') .then(function (res) { expect(res.status).to.equal(300); done(); }); }); it('respond with a string', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: 'a string' } }); fetch('http://it.at.there') .then(function (res) { expect(res.status).to.equal(200); res.text().then(function (text) { expect(text).to.equal('a string'); done(); }); }); }); it('respond with a json', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: {an: 'object'} } }); fetch('http://it.at.there') .then(function (res) { expect(res.status).to.equal(200); res.json().then(function (json) { expect(json).to.eql({an: 'object'}); done(); }); }); }); it('respond with a status', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: {status: 404} } }); fetch('http://it.at.there') .then(function (res) { expect(res.status).to.equal(404); done(); }) .catch(err); }); it('respond with a complex response, including headers', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: { status: 202, body: {an: 'object'}, headers: { header: 'val' } } } }); fetch('http://it.at.there') .then(function (res) { expect(res.status).to.equal(202); expect(res.headers.get('header')).to.equal('val'); res.json().then(function (json) { expect(json).to.eql({an: 'object'}); done(); }); }); }); it('imitate a failed request', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: { throws: 'Oh no' } } }); fetch('http://it.at.there') .catch(function (err) { expect(err).to.equal('Oh no'); done(); }); }); it('construct a response based on the request', function (done) { fetchMock.mock({ routes: { name: 'route', matcher: 'http://it.at.there', response: function (url, opts) { return url + opts.headers.header; } } }); fetch('http://it.at.there', {headers: {header: 'val'}}) .then(function (res) { expect(res.status).to.equal(200); return res.text().then(function (text) { expect(text).to.equal('http://it.at.thereval'); done(); }); }); }); }); }); describe('persistent route config', function () { beforeEach(function () { try { fetchMock.restore(); } catch (e) {} fetchMock.unregisterRoute(); }); it('register a single route', function (done) { fetchMock.registerRoute('route', 'http://it.at.there', 'a string'); fetchMock.mock(); fetch('http://it.at.there') .then(function () { expect(fetchMock.calls('route').length).to.equal(1); done(); }); }); it('register a single route as an object', function (done) { fetchMock.registerRoute({ name: 'route', matcher: 'http://it.at.there', response: 'ok' }); fetchMock.mock(); fetch('http://it.at.there') .then(function () { expect(fetchMock.calls('route').length).to.equal(1); done(); }); }); it('register multiple routes', function (done) { fetchMock.registerRoute([{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: 'http://it.at.here', response: 'ok' }]); fetchMock.mock(); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here')]) .then(function (res) { expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(1); done(); }); }); it('expects unique route names', function () { expect(function () { fetchMock.registerRoute([{ name: 'route', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route', matcher: 'http://it.at.here', response: 'ok' }]); fetchMock(); }).to.throw(); }); it('register routes multiple times', function () { fetchMock.registerRoute('route1', 'http://it.at.there', 'a string'); fetchMock.registerRoute('route2', 'http://it.at.here', 'a string'); fetchMock.mock(); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here')]) .then(function (res) { expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(1); done(); }) }); it('unregister a single route', function (done) { fetchMock.registerRoute([{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: 'http://it.at.here', response: 'ok' }, { name: 'route3', matcher: 'http://it.at.where', response: 'ok' }]); fetchMock.unregisterRoute('route2'); fetchMock.mock(); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here')]) .then(function (res) { expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(0); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }); }); it('unregister multiple routes', function (done) { fetchMock.registerRoute([{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: 'http://it.at.here', response: 'ok' }, { name: 'route3', matcher: 'http://it.at.where', response: 'ok' }]); fetchMock.unregisterRoute(['route1', 'route2']); fetchMock.mock(); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here'), fetch('http://it.at.where')]) .then(function (res) { expect(fetchMock.calls('route3').length).to.equal(1); expect(fetchMock.calls('route1').length).to.equal(0); expect(fetchMock.calls('route2').length).to.equal(0); expect(fetchMock.calls('__unmatched').length).to.equal(2); done(); }); }); it('preserve registered routes from test to test', function (done) { fetchMock.registerRoute('route', 'http://it.at.there', 'a string'); fetchMock.mock(); fetch('http://it.at.there') .then(function () { expect(fetchMock.calls('route').length).to.equal(1); fetchMock.restore(); expect(fetchMock.calls('route').length).to.equal(0); fetchMock.mock(); fetch('http://it.at.there') .then(function () { expect(fetchMock.calls('route').length).to.equal(1); fetchMock.restore(); done(); }); }); }); it('use selection of registered routes', function (done) { fetchMock.registerRoute([{ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }, { name: 'route2', matcher: 'http://it.at.here', response: 'ok' }, { name: 'route3', matcher: 'http://it.at.where', response: 'ok' }]); fetchMock.mock({ routes: ['route3', 'route1'] }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here'), fetch('http://it.at.where')]) .then(function (res) { expect(fetchMock.calls('route3').length).to.equal(1); expect(fetchMock.calls('route1').length).to.equal(1); expect(fetchMock.calls('route2').length).to.equal(0); expect(fetchMock.calls('__unmatched').length).to.equal(1); done(); }); }); it('mix one off routes with registered routes', function (done) { fetchMock.registerRoute({ name: 'route1', matcher: 'http://it.at.there', response: 'ok' }); fetchMock.mock({ routes: [{ name: 'route2', matcher: 'http://it.at.here', response: 'ok' }, 'route1'] }); Promise.all([fetch('http://it.at.there'), fetch('http://it.at.here')]) .then(function (res) { expect(fetchMock.calls('route2').length).to.equal(1); expect(fetchMock.calls('route1').length).to.equal(1); done(); }); }); it('apply routes in specified order', function (done) { fetchMock.registerRoute({ name: 'route1', matcher: 'http://it.at.here', response: 'ok' }); fetchMock.mock({ routes: [{ name: 'route2', matcher: 'http://it.at.here', response: 'ok' }, 'route1'] }); fetch('http://it.at.here') .then(function (res) { expect(fetchMock.calls('route2').length).to.equal(1); expect(fetchMock.calls('route1').length).to.equal(0); done(); }); }); it('override response for a registered route', function (done) { fetchMock.registerRoute({ name: 'route1', matcher: 'http://it.at.here', response: 'ok' }); fetchMock.mock({ responses: { route1: 'changed my mind' } }); fetch('http://it.at.here') .then(function (res) { res.text().then(function (text) { expect(text).to.equal('changed my mind'); done(); }); }); }); it('apply overrides when mock already mocking', function (done) { fetchMock.registerRoute({ name: 'route1', matcher: 'http://it.at.here', response: 'ok' }); fetchMock.mock(); fetchMock.reMock({ responses: { route1: 'changed my mind' } }); fetch('http://it.at.here') .then(function (res) { res.text().then(function (text) { expect(text).to.equal('changed my mind'); done(); }); }); }); }); }); }