diff --git a/docs/docs/@fetch-mock/core/route/matcher.md b/docs/docs/@fetch-mock/core/route/matcher.md index b9b1a7fc..ac7abe04 100644 --- a/docs/docs/@fetch-mock/core/route/matcher.md +++ b/docs/docs/@fetch-mock/core/route/matcher.md @@ -141,7 +141,7 @@ This option can also be [set in the global configuration](/fetch-mock/docs/@fetc For use cases not covered by all the built in matchers, a custom function can be used. It should return `true` to indicate a route should respond to a request. It will be passed the `url` and `options` `fetch` was called with. If `fetch` was called with a `Request` instance, it will be passed `url` and `options` inferred from the `Request` instance, with the original `Request` available as a third argument. -As well as being passed as a standalone argument, it can also be added to the matcher object as the property `{func: ...}` when combining with other matchers or options. +As well as being passed as a standalone argument, it can also be added to the matcher object as the property `{matcherFunction: ...}` when combining with other matchers or options. ### Examples diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.js index 08208233..3f57aa07 100644 --- a/packages/core/src/CallHistory.js +++ b/packages/core/src/CallHistory.js @@ -125,7 +125,7 @@ class CallHistory { } } else { if (isUrlMatcher(filter)) { - options = { matcher: filter, ...(options || {}) }; + options = { url: filter, ...(options || {}) }; } else { options = { ...filter, ...(options || {}) }; } diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index c24ba2f7..50f540c5 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -196,7 +196,7 @@ const getFullUrlMatcher = (route, matcherUrl, query) => { /** * @type {MatcherGenerator} */ -const getFunctionMatcher = ({ func }) => func; +const getFunctionMatcher = ({ matcherFunction }) => matcherFunction; /** * @type {MatcherGenerator} */ @@ -236,6 +236,6 @@ export const builtInMatchers = [ { name: 'headers', matcher: getHeaderMatcher }, { name: 'params', matcher: getParamsMatcher }, { name: 'body', matcher: getBodyMatcher, usesBody: true }, - { name: 'func', matcher: getFunctionMatcher }, + { name: 'matcherFunction', matcher: getFunctionMatcher }, { name: 'url', matcher: getUrlMatcher }, ]; diff --git a/packages/core/src/Route.js b/packages/core/src/Route.js index e7c7814a..1341b44e 100644 --- a/packages/core/src/Route.js +++ b/packages/core/src/Route.js @@ -1,5 +1,5 @@ //@type-check -import { builtInMatchers, isUrlMatcher, isFunctionMatcher } from './Matchers'; +import { builtInMatchers } from './Matchers'; import statusTextMap from './StatusTextMap'; /** @typedef {import('./Matchers').RouteMatcher} RouteMatcher */ @@ -41,7 +41,7 @@ import statusTextMap from './StatusTextMap'; * @property {{ [key: string]: string }} [query] * @property {{ [key: string]: string }} [params] * @property {object} [body] - * @property {RouteMatcherFunction} [func] + * @property {RouteMatcherFunction} [matcherFunction] * @property {RouteMatcher} [matcher] * @property {RouteMatcherUrl} [url] * @property {RouteResponse | RouteResponseFunction} [response] @@ -134,13 +134,6 @@ class Route { if (this.config.method) { this.config.method = this.config.method.toLowerCase(); } - if (isUrlMatcher(this.config.matcher)) { - this.config.url = this.config.matcher; - delete this.config.matcher; - } - if (isFunctionMatcher(this.config.matcher)) { - this.config.func = this.config.matcher; - } } /** * @returns {void} diff --git a/packages/core/src/Router.js b/packages/core/src/Router.js index 7fed0edb..7e3c0fb2 100644 --- a/packages/core/src/Router.js +++ b/packages/core/src/Router.js @@ -278,8 +278,8 @@ export default class Router { if (typeof response[name] === 'function') { //@ts-ignore return new Proxy(response[name], { - apply: (func, thisArg, args) => { - const result = func.apply(response, args); + apply: (matcherFunction, thisArg, args) => { + const result = matcherFunction.apply(response, args); if (result.then) { pendingPromises.push( result.catch(/** @type {function(): void} */ () => undefined), @@ -318,8 +318,10 @@ export default class Router { addRoute(matcher, response, nameOrOptions) { /** @type {RouteConfig} */ const config = {}; - if (isUrlMatcher(matcher) || isFunctionMatcher(matcher)) { - config.matcher = matcher; + if (isUrlMatcher(matcher)) { + config.url = matcher; + } else if (isFunctionMatcher(matcher)) { + config.matcherFunction = matcher; } else { Object.assign(config, matcher); } @@ -366,14 +368,7 @@ export default class Router { } this.fallbackRoute = new Route({ - matcher: (url, options) => { - if (this.config.warnOnFallback) { - console.warn( - `Unmatched ${(options && options.method) || 'GET'} to ${url}`, - ); // eslint-disable-line - } - return true; - }, + matcherFunction: () => true, response: response || 'ok', ...this.config, }); diff --git a/packages/core/src/__tests__/Matchers/express.test.js b/packages/core/src/__tests__/Matchers/express.test.js index 4973d6db..0f19ef11 100644 --- a/packages/core/src/__tests__/Matchers/express.test.js +++ b/packages/core/src/__tests__/Matchers/express.test.js @@ -4,7 +4,7 @@ import Route from '../../Route.js'; describe('express path parameter matching', () => { it('can match a path parameters', () => { const route = new Route({ - matcher: 'express:/type/:instance', + url: 'express:/type/:instance', response: 200, params: { instance: 'b' }, }); @@ -15,7 +15,7 @@ describe('express path parameter matching', () => { it('can match multiple path parameters', () => { const route = new Route({ - matcher: 'express:/:type/:instance', + url: 'express:/:type/:instance', response: 200, params: { instance: 'b', type: 'cat' }, }); @@ -28,7 +28,7 @@ describe('express path parameter matching', () => { it('can match a path parameter on a full url', () => { const route = new Route({ - matcher: 'express:/type/:instance', + url: 'express:/type/:instance', response: 200, params: { instance: 'b' }, }); @@ -38,7 +38,7 @@ describe('express path parameter matching', () => { }); it('can match fully qualified url', () => { - const route = new Route({ matcher: 'express:/apps/:id', response: 200 }); + const route = new Route({ url: 'express:/apps/:id', response: 200 }); expect(route.matcher('https://api.example.com/apps/abc')).toBe(true); }); diff --git a/packages/core/src/__tests__/Matchers/function.test.js b/packages/core/src/__tests__/Matchers/function.test.js index 46af89bc..d0f4d0db 100644 --- a/packages/core/src/__tests__/Matchers/function.test.js +++ b/packages/core/src/__tests__/Matchers/function.test.js @@ -4,7 +4,7 @@ import Route from '../../Route.js'; describe('function matching', () => { it('match using custom function', () => { const route = new Route({ - matcher: (url, opts) => + matcherFunction: (url, opts) => url.indexOf('logged-in') > -1 && opts && opts.headers && @@ -27,7 +27,7 @@ describe('function matching', () => { it('match using custom function using request body', () => { const route = new Route({ - matcher: (url, opts) => opts.body === 'a string', + matcherFunction: (url, opts) => opts.body === 'a string', response: 200, }); expect(route.matcher('http://a.com/logged-in')).toBe(false); @@ -41,9 +41,9 @@ describe('function matching', () => { it('match using custom function alongside other matchers', () => { const route = new Route({ - matcher: 'end:profile', + url: 'end:profile', response: 200, - func: (url, opts) => + matcherFunction: (url, opts) => opts && opts.headers && opts.headers.authorized === true, }); diff --git a/packages/core/src/__tests__/Matchers/method.test.js b/packages/core/src/__tests__/Matchers/method.test.js index 92eb6c8d..3d8a940d 100644 --- a/packages/core/src/__tests__/Matchers/method.test.js +++ b/packages/core/src/__tests__/Matchers/method.test.js @@ -4,7 +4,7 @@ import Route from '../../Route.js'; describe('method matching', () => { it('match any method by default', () => { - const route = new Route({ matcher: '*', response: 200 }); + const route = new Route({ url: '*', response: 200 }); expect(route.matcher('http://a.com/', { method: 'GET' })).toBe(true); expect(route.matcher('http://a.com/', { method: 'POST' })).toBe(true); @@ -43,7 +43,7 @@ describe('method matching', () => { it('can be used alongside function matchers', () => { const route = new Route({ method: 'POST', - matcher: (url) => /a\.com/.test(url), + matcherFunction: (url) => /a\.com/.test(url), response: 200, }); diff --git a/packages/core/src/__tests__/Matchers/route-config-object.test.js b/packages/core/src/__tests__/Matchers/route-config-object.test.js index b9ad11a3..fff37d0b 100644 --- a/packages/core/src/__tests__/Matchers/route-config-object.test.js +++ b/packages/core/src/__tests__/Matchers/route-config-object.test.js @@ -5,7 +5,7 @@ import Route from '../../Route.js'; // as it's mainly about the shape of optiosn passed into to addRoute describe('matcher object', () => { it('use matcher object with matcher property', () => { - const route = new Route({ matcher: 'http://a.com', response: 200 }); + const route = new Route({ url: 'http://a.com', response: 200 }); expect(route.matcher('http://a.com')).toBe(true); }); @@ -14,10 +14,10 @@ describe('matcher object', () => { expect(route.matcher('http://a.com')).toBe(true); }); - it('can use matcher and url simultaneously', () => { + it('can use function and url simultaneously', () => { const route = new Route({ url: 'end:path', - matcher: (url, opts) => + matcherFunction: (url, opts) => opts && opts.headers && opts.headers.authorized === true, response: 200, }); @@ -41,11 +41,11 @@ describe('matcher object', () => { expect(route.matcher('http://a.com')).toBe(true); }); - //TODO be strionger on discouraging this - it.skip('deprecated message on using func (prefer matcher)', () => { + //TODO be stronger on discouraging this + it.skip('deprecated message on using matcherFunction (prefer matcher)', () => { new Route({ url: 'end:profile', - func: (url, opts) => + matcherFunction: (url, opts) => opts && opts.headers && opts.headers.authorized === true, response: 200, }); @@ -115,9 +115,6 @@ describe('matcher object', () => { ).toBe(true); }); - // TODO new tests for how multiple routes that match can be addeed - it.skip('support setting overwrite routes on matcher parameter', () => {}); - it('support setting matchPartialBody on matcher parameter', () => { const route = new Route({ body: { a: 1 }, diff --git a/packages/core/src/__tests__/Matchers/url.test.js b/packages/core/src/__tests__/Matchers/url.test.js index f49bcfa3..1178cad5 100644 --- a/packages/core/src/__tests__/Matchers/url.test.js +++ b/packages/core/src/__tests__/Matchers/url.test.js @@ -3,7 +3,7 @@ import Route from '../../Route.js'; describe('url matching', () => { it('match exact strings', () => { - const route = new Route({ matcher: 'http://a.com/path', response: 200 }); + const route = new Route({ url: 'http://a.com/path', response: 200 }); expect(route.matcher('http://a.com/pat')).toBe(false); expect(route.matcher('http://a.com/paths')).toBe(false); expect(route.matcher('http://a.co/path')).toBe(false); @@ -12,32 +12,32 @@ describe('url matching', () => { }); it('match string objects', () => { - const route = new Route({ matcher: 'http://a.com/path', response: 200 }); + const route = new Route({ url: 'http://a.com/path', response: 200 }); expect(route.matcher(new String('http://a.com/path'))).toBe(true); // eslint-disable-line no-new-wrappers }); it('match exact strings with relative url', () => { - const route = new Route({ matcher: '/path', response: 200 }); + const route = new Route({ url: '/path', response: 200 }); expect(route.matcher('/pat')).toBe(false); expect(route.matcher('/paths')).toBe(false); expect(route.matcher('/path')).toBe(true); }); it('match exact string against URL object', () => { - const route = new Route({ matcher: 'http://a.com/path', response: 200 }); + const route = new Route({ url: 'http://a.com/path', response: 200 }); const url = new URL('http://a.com/path'); expect(route.matcher(url)).toBe(true); }); it('match using URL object as matcher', () => { const url = new URL('http://a.com/path'); - const route = new Route({ matcher: url, response: 200 }); + const route = new Route({ url: url, response: 200 }); expect(route.matcher('http://a.com/path')).toBe(true); }); it('match begin: keyword', () => { const route = new Route({ - matcher: 'begin:http://a.com/path', + url: 'begin:http://a.com/path', response: 200, }); @@ -48,7 +48,7 @@ describe('url matching', () => { }); it('match end: keyword', () => { - const route = new Route({ matcher: 'end:com/path', response: 200 }); + const route = new Route({ url: 'end:com/path', response: 200 }); expect(route.matcher('http://a.com/paths')).toBe(false); expect(route.matcher('http://a.com/pat')).toBe(false); expect(route.matcher('http://a.com/path')).toBe(true); @@ -56,14 +56,14 @@ describe('url matching', () => { }); it('match glob: keyword', () => { - const route = new Route({ matcher: 'glob:/its/*/*', response: 200 }); + const route = new Route({ url: 'glob:/its/*/*', response: 200 }); expect(route.matcher('/its/alive')).toBe(false); expect(route.matcher('/its/a/boy')).toBe(true); expect(route.matcher('/its/a/girl')).toBe(true); }); it('match express: keyword', () => { - const route = new Route({ matcher: 'express:/its/:word', response: 200 }); + const route = new Route({ url: 'express:/its/:word', response: 200 }); expect(route.matcher('/its')).toBe(false); expect(route.matcher('/its/')).toBe(false); @@ -72,7 +72,7 @@ describe('url matching', () => { }); it('match path: keyword', () => { - const route = new Route({ matcher: 'path:/its/:word', response: 200 }); + const route = new Route({ url: 'path:/its/:word', response: 200 }); expect(route.matcher('/its/boy')).toBe(false); expect(route.matcher('/its/:word/still')).toBe(false); @@ -81,14 +81,14 @@ describe('url matching', () => { }); it('match wildcard string', () => { - const route = new Route({ matcher: '*', response: 200 }); + const route = new Route({ url: '*', response: 200 }); expect(route.matcher('http://a.com')).toBe(true); }); it('match regular expressions', () => { const rx = /http\:\/\/a\.com\/\d+/; - const route = new Route({ matcher: rx, response: 200 }); + const route = new Route({ url: rx, response: 200 }); expect(route.matcher('http://a.com/')).toBe(false); expect(route.matcher('http://a.com/abcde')).toBe(false); @@ -96,29 +96,29 @@ describe('url matching', () => { }); it('match relative urls', () => { - const route = new Route({ matcher: '/a.com/', response: 200 }); + const route = new Route({ url: '/a.com/', response: 200 }); expect(route.matcher('/a.com/')).toBe(true); }); it('match relative urls with dots', () => { - const route = new Route({ matcher: '/it.at/there/', response: 200 }); + const route = new Route({ url: '/it.at/there/', response: 200 }); expect(route.matcher('/it.at/not/../there/')).toBe(true); expect(route.matcher('./it.at/there/')).toBe(true); }); it('match absolute urls with dots', () => { - const route = new Route({ matcher: 'http://it.at/there/', response: 200 }); + const route = new Route({ url: 'http://it.at/there/', response: 200 }); expect(route.matcher('http://it.at/not/../there/')).toBe(true); }); describe('host normalisation', () => { it('match exact pathless urls regardless of trailing slash', () => { - const route = new Route({ matcher: 'http://a.com/', response: 200 }); + const route = new Route({ url: 'http://a.com/', response: 200 }); expect(route.matcher('http://a.com/')).toBe(true); expect(route.matcher('http://a.com')).toBe(true); - const route2 = new Route({ matcher: 'http://b.com', response: 200 }); + const route2 = new Route({ url: 'http://b.com', response: 200 }); expect(route2.matcher('http://b.com/')).toBe(true); expect(route2.matcher('http://b.com')).toBe(true); }); @@ -127,7 +127,7 @@ describe('url matching', () => { describe('data: URLs', () => { it('match exact strings', () => { const route = new Route({ - matcher: 'data:text/plain,path', + url: 'data:text/plain,path', response: 200, }); expect(route.matcher('data:text/plain,pat')).toBe(false); @@ -137,7 +137,7 @@ describe('url matching', () => { }); it('match exact string against URL object', () => { const route = new Route({ - matcher: 'data:text/plain,path', + url: 'data:text/plain,path', response: 200, }); const url = new URL('data:text/plain,path'); @@ -145,12 +145,12 @@ describe('url matching', () => { }); it('match using URL object as matcher', () => { const url = new URL('data:text/plain,path'); - const route = new Route({ matcher: url, response: 200 }); + const route = new Route({ url: url, response: 200 }); expect(route.matcher('data:text/plain,path')).toBe(true); }); it('match begin: keyword', () => { const route = new Route({ - matcher: 'begin:data:text/plain', + url: 'begin:data:text/plain', response: 200, }); expect(route.matcher('http://a.com/path')).toBe(false); @@ -159,25 +159,25 @@ describe('url matching', () => { expect(route.matcher('data:text/plain;base64,cGF0aA')).toBe(true); }); it('match end: keyword', () => { - const route = new Route({ matcher: 'end:sky', response: 200 }); + const route = new Route({ url: 'end:sky', response: 200 }); expect(route.matcher('data:text/plain,blue lake')).toBe(false); expect(route.matcher('data:text/plain,blue sky research')).toBe(false); expect(route.matcher('data:text/plain,blue sky')).toBe(true); expect(route.matcher('data:text/plain,grey sky')).toBe(true); }); it('match glob: keyword', () => { - const route = new Route({ matcher: 'glob:data:* sky', response: 200 }); + const route = new Route({ url: 'glob:data:* sky', response: 200 }); expect(route.matcher('data:text/plain,blue lake')).toBe(false); expect(route.matcher('data:text/plain,blue sky')).toBe(true); expect(route.matcher('data:text/plain,grey sky')).toBe(true); }); it('match wildcard string', () => { - const route = new Route({ matcher: '*', response: 200 }); + const route = new Route({ url: '*', response: 200 }); expect(route.matcher('data:text/plain,path')).toBe(true); }); it('match regular expressions', () => { const rx = /data\:text\/plain,\d+/; - const route = new Route({ matcher: rx, response: 200 }); + const route = new Route({ url: rx, response: 200 }); expect(route.matcher('data:text/html,12345')).toBe(false); expect(route.matcher('data:text/plain,path')).toBe(false); expect(route.matcher('data:text/plain,12345')).toBe(true);