diff --git a/docs/docs/@fetch-mock/core/route/matcher.md b/docs/docs/@fetch-mock/core/route/matcher.md index 5edac634..a7f60ac9 100644 --- a/docs/docs/@fetch-mock/core/route/matcher.md +++ b/docs/docs/@fetch-mock/core/route/matcher.md @@ -71,9 +71,9 @@ Match a url that satisfies an [express style path](https://www.npmjs.com/package When the `express:` keyword is used in a string matcher, it can be combined with the `{params: ...}` matcher to match only requests whose express parameters evaluate to certain values. e.g. -``` +```js { - express: "/:section/user/:user", + url: "express:/:section/user/:user", params: {"section": "feed", "user": "geoff"} } ``` @@ -83,6 +83,19 @@ The values of express parameters are made available in the `expressParams` prope - [Inspecting call history](/fetch-mock/docs/@fetch-mock/core/CallHistory#calllog-schema) - [Using a function to construct a response](/fetch-mock/docs/@fetch-mock/core/route/response#function) +### Multiple url matchers + +All of the above (with the exception of the full url matcher) can be combined in an object in order to match multiple patterns at once e.g. + +```js +{ + url: { + begin: 'https', + path: '/could/be/any/host' + } +} +``` + ## Other matching criteria ### method diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index b4bff45d..d0abd8fc 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -7,7 +7,16 @@ import { isSubsetOf } from 'is-subset-of'; import { dequal as isEqual } from 'dequal'; import { normalizeHeaders, getPath, normalizeUrl } from './RequestUtils.js'; -/** @typedef {string | RegExp | URL} RouteMatcherUrl */ +/** + * @typedef URLMatcherObject + * @property {string} [begin] + * @property {string} [end] + * @property {string} [glob] + * @property {string} [express] + * @property {string} [path] + * @property {RegExp} [regexp] + */ +/** @typedef {string | RegExp | URL | URLMatcherObject} RouteMatcherUrl */ /** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ /** @typedef {function(CallLog): boolean} RouteMatcherFunction */ /** @typedef {function(RouteConfig): RouteMatcherFunction} MatcherGenerator */ @@ -211,6 +220,15 @@ const getBodyMatcher = (route) => { */ const getFunctionMatcher = ({ matcherFunction }) => matcherFunction; +/** + * @param {RegExp} regexp + * @returns {RouteMatcherFunction} + */ +const getRegexpMatcher = + (regexp) => + ({ url }) => + regexp.test(url); + /** * * @param {RouteConfig} route @@ -247,7 +265,7 @@ const getUrlMatcher = (route) => { } if (matcherUrl instanceof RegExp) { - return ({ url }) => matcherUrl.test(url); + return getRegexpMatcher(matcherUrl); } if (matcherUrl instanceof URL) { if (matcherUrl.href) { @@ -266,6 +284,20 @@ const getUrlMatcher = (route) => { } return getFullUrlMatcher(route, matcherUrl, query); } + + if (typeof matcherUrl === 'object') { + const matchers = Object.entries(matcherUrl).map(([key, pattern]) => { + if (key === 'regexp') { + return getRegexpMatcher(pattern); + } else if (key in stringMatchers) { + return stringMatchers[key](pattern); + } else { + throw new Error(`unrecognised url matching pattern: ${key}`); + } + }); + + return (route) => matchers.every((matcher) => matcher(route)); + } }; /** @type {MatcherDefinition[]} */ diff --git a/packages/core/src/__tests__/Matchers/url.test.js b/packages/core/src/__tests__/Matchers/url.test.js index 938efb4b..e25dc61b 100644 --- a/packages/core/src/__tests__/Matchers/url.test.js +++ b/packages/core/src/__tests__/Matchers/url.test.js @@ -111,6 +111,19 @@ describe('url matching', () => { expect(route.matcher({ url: 'http://it.at/not/../there/' })).toBe(true); }); + it('match with multiple url patterns at once', () => { + const route = new Route({ + url: { + begin: 'http', + end: 'jam', + path: '/jar/of/jam', + express: '/:container/of/:stuff', + }, + response: 200, + }); + expect(route.matcher({ url: 'http://a.com/jar/of/jam' })).toBe(true); + }); + describe('host normalisation', () => { it('match exact pathless urls regardless of trailing slash', () => { const route = new Route({ url: 'http://a.com/', response: 200 }); diff --git a/packages/core/types/Matchers.d.ts b/packages/core/types/Matchers.d.ts index 5809e6ee..ac395ef7 100644 --- a/packages/core/types/Matchers.d.ts +++ b/packages/core/types/Matchers.d.ts @@ -3,7 +3,15 @@ export function isFunctionMatcher(matcher: RouteMatcher | RouteConfig): matcher export const builtInMatchers: MatcherDefinition[]; export type RouteConfig = import("./Route.js").RouteConfig; export type CallLog = import("./CallHistory.js").CallLog; -export type RouteMatcherUrl = string | RegExp | URL; +export type URLMatcherObject = { + begin?: string; + end?: string; + glob?: string; + express?: string; + path?: string; + regexp?: RegExp; +}; +export type RouteMatcherUrl = string | RegExp | URL | URLMatcherObject; export type UrlMatcherGenerator = (arg0: string) => RouteMatcherFunction; export type RouteMatcherFunction = (arg0: CallLog) => boolean; export type MatcherGenerator = (arg0: RouteConfig) => RouteMatcherFunction;