Skip to content

Commit 9a19360

Browse files
authored
Merge pull request #151 from stas-pavlov/main
Add support for withUrlMatching matching
2 parents 3ee5f31 + 62128c2 commit 9a19360

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

src/rules/base-rule-builder.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import {
3030
CallbackMatcher,
3131
HostnameMatcher,
3232
PortMatcher,
33-
ProtocolMatcher
33+
ProtocolMatcher,
34+
RegexUrlMatcher
3435
} from "./matchers";
3536

3637
/**
@@ -236,14 +237,23 @@ export abstract class BaseRuleBuilder {
236237
}
237238

238239
/**
239-
* Match only requests that sent with the given protocol.
240+
* Match only requests sent with the given protocol.
240241
* @category Matching
241242
*/
242243
withProtocol(protocol: "http" | "https" | "ws" | "wss"): this {
243244
this.matchers.push(new ProtocolMatcher(protocol));
244245
return this;
245246
}
246247

248+
/**
249+
* Match only requests whose absolute url matches the given RegExp.
250+
* @category Matching
251+
*/
252+
withUrlMatching(pattern: RegExp): this {
253+
this.matchers.push(new RegexUrlMatcher(pattern));
254+
return this;
255+
}
256+
247257
/**
248258
* Match only requests when the callback returns true
249259
* @category Matching

src/rules/matchers.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,32 @@ export class RegexPathMatcher extends Serializable implements RequestMatcher {
260260

261261
}
262262

263+
export class RegexUrlMatcher extends Serializable implements RequestMatcher {
264+
readonly type = 'regex-url';
265+
266+
readonly regexSource: string;
267+
readonly regexFlags: string;
268+
269+
constructor(regex: RegExp) {
270+
super();
271+
this.regexSource = regex.source;
272+
this.regexFlags = regex.flags;
273+
}
274+
275+
matches(request: OngoingRequest) {
276+
const absoluteUrl = normalizeUrl(request.url);
277+
278+
// Test the matcher against the full URL
279+
const urlMatcher = new RegExp(this.regexSource, this.regexFlags);
280+
return urlMatcher.test(absoluteUrl);
281+
}
282+
283+
explain() {
284+
return `matching URL /${unescapeRegexp(this.regexSource)}/${this.regexFlags ?? ''}`;
285+
}
286+
287+
}
288+
263289
export class HeaderMatcher extends Serializable implements RequestMatcher {
264290
readonly type = 'header';
265291

@@ -590,6 +616,7 @@ export const MatcherLookup = {
590616
'port': PortMatcher,
591617
'simple-path': SimplePathMatcher,
592618
'regex-path': RegexPathMatcher,
619+
'regex-url': RegexUrlMatcher,
593620
'header': HeaderMatcher,
594621
'query': QueryMatcher,
595622
'exact-query-string': ExactQueryMatcher,

test/integration/matchers/method-path-matching.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,34 @@ responses by hand.`);
120120
await expect(result).to.have.responseText('Fake file');
121121
});
122122

123+
it("should regex match requests for a matching full URL", async () => {
124+
await server.forGet(/localhost:\d+\/[\w\-]+.txt/).thenReply(200, 'Fake file');
125+
126+
let result = await fetch(server.urlFor('/matching-file.txt'));
127+
128+
await expect(result).to.have.responseText('Fake file');
129+
});
130+
131+
it("should not negative-regex match requests by full URL with forX matchers", async () => {
132+
// It's not so much that this is desired behaviour - more that it follows from the
133+
// definition of forGet matching, and we want to preserve it for now, as opposed
134+
// to usage of withUrlMatching (below) which does _not_ behave like this.
135+
136+
await server.forGet(/^(?!.*localhost)/).thenReply(200, 'Fake file');
137+
138+
let result = await fetch(server.urlFor('/matching-file.txt'));
139+
140+
await expect(result).to.have.responseText('Fake file');
141+
});
142+
143+
it("should negative-regex match requests by full URL with withUrlMatching", async () => {
144+
await server.forGet().withUrlMatching(/^(?!.*localhost)/).thenReply(200, 'Fake file');
145+
146+
let result2 = await fetch(server.urlFor('/matching-file.txt'));
147+
148+
await expect(result2).to.have.responseText(/^.*No rules*./);
149+
});
150+
123151
it("should reject requests for the wrong path", async () => {
124152
await server.forGet("/specific-endpoint").thenReply(200, "mocked data");
125153

0 commit comments

Comments
 (0)