Skip to content

Commit 2a7a3c0

Browse files
committed
Support fallbackToNetwork
#6 true: Unhandled calls fall through to the network false: Unhandled calls throw an error 'always': All calls fall through to the network, effectively disabling fetch-mock.
1 parent 82c0890 commit 2a7a3c0

File tree

4 files changed

+104
-6
lines changed

4 files changed

+104
-6
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ So, I create one by myself.
1818
- [x] Proxy for other api server
1919
- [x] Delay for global and specific path
2020
- [ ] Support flexible fallback to network([#6](https://github.com/WhatAKitty/react-native-fetch-mock/issues/6))
21+
- [ ] Support inline valiation(such as: '/api/users/{userid:[a-z|A-Z]}')
2122

2223
## Usage
2324

@@ -70,6 +71,7 @@ if (__dev__) {
7071
'http://www.google.com',
7172
'/foo(.*)',
7273
],
74+
fallbackToNetwork: true, // ['true', 'false', 'always'], true: Unhandled calls fall through to the network;false: Unhandled calls throw an error; 'always': All calls fall through to the network, effectively disabling react-native-fetch-mock
7375
proxy: [{
7476
path: '/path/for/proxy(.*)',
7577
target: 'http://other.proxy.server',

dist/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,21 @@ Object.defineProperty(exports,"__esModule",{value:true});exports.Mock=undefined;
145145

146146

147147

148-
default;}});var _pathToRegexp=require('path-to-regexp');var _pathToRegexp2=_interopRequireDefault(_pathToRegexp);var _util=require('./util');var _response=require('./response');var _response2=_interopRequireDefault(_response);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}var FetchMock=function(){function FetchMock(required){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{fetch:function fetch(){},exclude:[],proxy:[],delay:2000};_classCallCheck(this,FetchMock);if('object'!==typeof required){throw new Error('There is no required defined.');}this.urls=[];this.raw=options.fetch;this.exclude=options.exclude||[];this.proxy=options.proxy||[];this.delayTime=options.delay;this.loadMocks=this.loadMocks.bind(this);this.loadMock=this.loadMock.bind(this);this.matchReqUrl=this.matchReqUrl.bind(this);this.isExclude=this.isExclude.bind(this);this.isProxied=this.isProxied.bind(this);this.fetch=this.fetch.bind(this);this.loadMocks(required);}_createClass(FetchMock,[{key:'loadMocks',value:function loadMocks(required){var _this=this;var __mocks__=required.default||required;var mocks=Object.keys(__mocks__);mocks.forEach(function(key){_this.loadMock(key,__mocks__[key]);});}},{key:'loadMock',value:function loadMock(key,mock){var _this2=this;if('object'!==typeof mock){if('function'===typeof mock){var items=key.split(' ');var method=items.length===2?items[0]:'GET';var url=items.length===2?items[1]:key;this.urls.push({method:method,url:url,func:mock});}return;}var keys=Object.keys(mock);keys.map(function(key){_this2.loadMock(key,mock[key]);});}},{key:'matchReqUrl',value:function matchReqUrl(request){var insideParams=void 0;var filters=this.urls.filter(function(uri){var obj=(0,_util.matchUrl)(uri.url,request.url);if(obj.result&&uri.method.toUpperCase()===request.method.toUpperCase()){insideParams=obj.params;return true;}return false;});if(!filters||filters.length==0)throw new Error('No url '+request.url+' is defined.');request.urlparams=insideParams;return{request:request,mock:filters[0]};}},{key:'isExclude',value:function isExclude(url){for(var i=0;i<this.exclude.length;i++){var excludeUrl=this.exclude[i];if(excludeUrl===url||(0,_pathToRegexp2.default)(''+excludeUrl).exec(url)!==null){return true;}}return false;}},{key:'isProxied',value:function isProxied(url){if(this.proxy.length===0)return false;var proxied=this.proxy.filter(function(item){return(0,_pathToRegexp2.default)(''+item.path).exec(url)!==null;});if(proxied.length>1)throw new Error(url+' proxied has two proxies, you should specific only one');return proxied[0];}},{key:'proxied',value:function proxied(url){var matches=void 0,proxied=void 0;this.proxy.forEach(function(item){var tmp=(0,_pathToRegexp2.default)(item.path).exec(url);if(tmp.length>1){matches=tmp;proxied=item;return false;}});return proxied.process?proxied.process(proxied,matches):proxied.target+'/'+matches[1];}},{key:'fetch',value:function fetch(url){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};if(this.isProxied(url)){url=this.proxied(url);}if(this.isExclude(url)){return this.raw(url,options);}var _matchReqUrl=this.matchReqUrl((0,_util.parseRequest)(url,options)),request=_matchReqUrl.request,mock=_matchReqUrl.mock;if('function'!==typeof mock.func){throw new Error('There is no url defined in __mocks__');}var obj=mock.func(request);if((0,_util.isNull)(obj)){throw'response data should not be undefined or null, it will be an object or an array at least';}if((0,_util.isNull)(obj.status)){obj={status:200,data:obj};}var response=new _response2.default(obj);var delayTime=options.delay||this.delayTime||0;return(0,_util.delay)(delayTime).then(function(){return Promise.resolve(response);});}}]);return FetchMock;}();exports.default=FetchMock;
148+
149+
150+
151+
152+
153+
154+
155+
156+
157+
158+
159+
160+
161+
162+
163+
164+
165+
default;}});var _pathToRegexp=require('path-to-regexp');var _pathToRegexp2=_interopRequireDefault(_pathToRegexp);var _util=require('./util');var _response=require('./response');var _response2=_interopRequireDefault(_response);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}var FetchMock=function(){function FetchMock(required){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{fetch:function fetch(){},exclude:[],fallbackToNetwork:false,proxy:[],delay:2000};_classCallCheck(this,FetchMock);if('object'!==typeof required){throw new Error('There is no required defined.');}this.urls=[];this.raw=options.fetch;this.exclude=options.exclude||[];this.fallbackToNetwork=options.fallbackToNetwork||false;this.proxy=options.proxy||[];this.delayTime=options.delay;this.loadMocks=this.loadMocks.bind(this);this.loadMock=this.loadMock.bind(this);this.matchReqUrl=this.matchReqUrl.bind(this);this.isExclude=this.isExclude.bind(this);this.isProxied=this.isProxied.bind(this);this.fetch=this.fetch.bind(this);this.loadMocks(required);}_createClass(FetchMock,[{key:'loadMocks',value:function loadMocks(required){var _this=this;var __mocks__=required.default||required;var mocks=Object.keys(__mocks__);mocks.forEach(function(key){_this.loadMock(key,__mocks__[key]);});}},{key:'loadMock',value:function loadMock(key,mock){var _this2=this;if('object'!==typeof mock){if('function'===typeof mock){var items=key.split(' ');var method=items.length===2?items[0]:'GET';var url=items.length===2?items[1]:key;this.urls.push({method:method,url:url,func:mock});}return;}var keys=Object.keys(mock);keys.map(function(key){_this2.loadMock(key,mock[key]);});}},{key:'matchReqUrl',value:function matchReqUrl(request){var insideParams=void 0;var filters=this.urls.filter(function(uri){var obj=(0,_util.matchUrl)(uri.url,request.url);if(obj.result&&uri.method.toUpperCase()===request.method.toUpperCase()){insideParams=obj.params;return true;}return false;});if(!filters||filters.length==0){return{matched:false};}request.urlparams=insideParams;return{matched:true,request:request,mock:filters[0]};}},{key:'isExclude',value:function isExclude(url){for(var i=0;i<this.exclude.length;i++){var excludeUrl=this.exclude[i];if(excludeUrl===url||(0,_pathToRegexp2.default)(''+excludeUrl).exec(url)!==null){return true;}}return false;}},{key:'isProxied',value:function isProxied(url){if(this.proxy.length===0)return false;var proxied=this.proxy.filter(function(item){return(0,_pathToRegexp2.default)(''+item.path).exec(url)!==null;});if(proxied.length>1)throw new Error(url+' proxied has two proxies, you should specific only one');return proxied[0];}},{key:'proxied',value:function proxied(url){var matches=void 0,proxied=void 0;this.proxy.forEach(function(item){var tmp=(0,_pathToRegexp2.default)(item.path).exec(url);if(tmp.length>1){matches=tmp;proxied=item;return false;}});return proxied.process?proxied.process(proxied,matches):proxied.target+'/'+matches[1];}},{key:'fetch',value:function fetch(url){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};if(this.isProxied(url)){url=this.proxied(url);}if(this.isExclude(url)||this.fallbackToNetwork==='always'){return this.raw(url,options);}var _matchReqUrl=this.matchReqUrl((0,_util.parseRequest)(url,options)),matched=_matchReqUrl.matched,request=_matchReqUrl.request,mock=_matchReqUrl.mock;if(!matched){if(this.fallbackToNetwork){return this.raw(url,options);}else{throw new Error('No url '+url+' is defined.');}}if('function'!==typeof mock.func){throw new Error('The url '+url+' defined in __mocks__ is not a function');}var obj=mock.func(request);if((0,_util.isNull)(obj)){throw'response data should not be undefined or null, it will be an object or an array at least';}if((0,_util.isNull)(obj.status)){obj={status:200,data:obj};}var response=new _response2.default(obj);var delayTime=options.delay||this.delayTime||0;return(0,_util.delay)(delayTime).then(function(){return Promise.resolve(response);});}}]);return FetchMock;}();exports.default=FetchMock;

src/index.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class FetchMock {
66
constructor(required, options = {
77
fetch: () => {},
88
exclude: [],
9+
fallbackToNetwork: false,
910
proxy: [],
1011
delay: 2000, // ms
1112
}) {
@@ -16,6 +17,7 @@ class FetchMock {
1617
this.urls = [];
1718
this.raw = options.fetch;
1819
this.exclude = options.exclude || [];
20+
this.fallbackToNetwork = options.fallbackToNetwork || false;
1921
this.proxy = options.proxy || [];
2022
this.delayTime = options.delay;
2123

@@ -67,9 +69,14 @@ class FetchMock {
6769
}
6870
return false;
6971
});
70-
if (!filters || filters.length == 0) throw new Error(`No url ${request.url} is defined.`);
72+
if (!filters || filters.length == 0) {
73+
return {
74+
matched: false,
75+
};
76+
}
7177
request.urlparams = insideParams;
7278
return {
79+
matched: true,
7380
request,
7481
mock: filters[0],
7582
};
@@ -114,15 +121,25 @@ class FetchMock {
114121
url = this.proxied(url);
115122
}
116123

117-
// using raw fetch while match exclude
118-
if (this.isExclude(url)) {
124+
// using raw fetch while match exclude or the fallbackToNetwork value is 'always'
125+
if (this.isExclude(url) || this.fallbackToNetwork === 'always') {
119126
// using raw fetch
120127
return this.raw(url, options);
121128
}
122129

123-
const { request, mock } = this.matchReqUrl(parseRequest(url, options));
130+
const { matched, request, mock } = this.matchReqUrl(parseRequest(url, options));
131+
if (!matched) {
132+
if (this.fallbackToNetwork) {
133+
// Unhandled calls fall through to the network
134+
return this.raw(url, options);
135+
} else {
136+
// Unhandled calls throw an error
137+
throw new Error(`No url ${url} is defined.`);
138+
}
139+
}
124140
if ('function' !== typeof mock.func) {
125-
throw new Error('There is no url defined in __mocks__');
141+
// the mock func is not a function
142+
throw new Error(`The url ${url} defined in __mocks__ is not a function`);
126143
}
127144
let obj = mock.func(request);
128145

test/fallbackToNetwork.test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import 'babel-polyfill';
2+
import expect from 'expect.js';
3+
import FetchMock, { Mock } from '../src';
4+
5+
const fetch1 = new FetchMock(require('../__mocks__'), {
6+
delay: 200, // delay 200ms
7+
fetch: require('isomorphic-fetch'),
8+
fallbackToNetwork: false
9+
}).fetch;
10+
describe('test fetch mock with fallbackToNetwork=false', () => {
11+
it('fetch /api/no-mocked', async () => {
12+
try {
13+
await fetch1('/api/no-mocked');
14+
} catch (e) {
15+
expect(e.toString()).to.be.eql('Error: No url /api/no-mocked is defined.')
16+
}
17+
}).timeout(20000);
18+
});
19+
20+
const fetch2 = new FetchMock(require('../__mocks__'), {
21+
delay: 200, // delay 200ms
22+
fetch: require('isomorphic-fetch'),
23+
fallbackToNetwork: true
24+
}).fetch;
25+
describe('test fetch mock with fallbackToNetwork=true', () => {
26+
it('fetch http://www.baidu.com', async () => {
27+
const response = await fetch2('http://www.baidu.com');
28+
const { status } = response;
29+
expect(status).to.be.eql(200);
30+
}).timeout(20000);
31+
32+
it('fetch /api/users/{userId}', async () => {
33+
const response = await fetch2('/api/users/123');
34+
const { status } = response;
35+
expect(status).to.be.eql(200);
36+
const data = await response.json();
37+
expect(data).not.to.be(undefined);
38+
expect(data).not.to.be.empty();
39+
expect(data).to.be.property('userId', '123');
40+
}).timeout(20000);
41+
});
42+
43+
const fetch3 = new FetchMock(require('../__mocks__'), {
44+
delay: 200, // delay 200ms
45+
fetch: require('isomorphic-fetch'),
46+
fallbackToNetwork: 'always'
47+
}).fetch;
48+
describe('test fetch mock with fallbackToNetwork=true', () => {
49+
it('fetch http://www.baidu.com', async () => {
50+
const response = await fetch3('http://www.baidu.com');
51+
const { status } = response;
52+
expect(status).to.be.eql(200);
53+
}).timeout(20000);
54+
55+
it('fetch /api/users/{userId}', async () => {
56+
try {
57+
await fetch3('/api/users/123');
58+
} catch (e) {
59+
expect(e.toString()).to.be.eql('Error: only absolute urls are supported')
60+
}
61+
}).timeout(20000);
62+
});

0 commit comments

Comments
 (0)