feat(urlMatch): simplify the url match
This commit is contained in:
parent
a4cb386b70
commit
55ad47c047
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
import * as debug from 'debug';
|
import * as debug from 'debug';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
|
import * as URL from 'url';
|
||||||
import { TimeoutError } from './errors';
|
import { TimeoutError } from './errors';
|
||||||
|
|
||||||
export const debugError = debug(`playwright:error`);
|
export const debugError = debug(`playwright:error`);
|
||||||
|
|
@ -167,81 +168,37 @@ class Helper {
|
||||||
throw new Error(`url match field "${name}" must be a string or a RegExp, got ${typeof match}`);
|
throw new Error(`url match field "${name}" must be a string or a RegExp, got ${typeof match}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static searchParamsMatch(params: URLSearchParams, match: types.SearchParamsMatch, strict: boolean, name: string): boolean {
|
static queryParamsMatch(params: { [key: string]: string | string[] | undefined; }, match: types.URLQueryParamsMatch, name: string): boolean {
|
||||||
if (typeof match !== 'object' || match === null)
|
if (typeof match !== 'object' || match === null)
|
||||||
throw new Error(`url match field "${name}" must be an object, got ${typeof match}`);
|
throw new Error(`url match field "${name}" must be an object, got ${typeof match}`);
|
||||||
const keys = new Set((params as any).keys()) as Set<string>;
|
for (const key of Object.keys(match)) {
|
||||||
if (strict && keys.size !== Object.keys(match).length)
|
const value = params[key];
|
||||||
return false;
|
if (!value)
|
||||||
for (const key of keys) {
|
|
||||||
let expected = [];
|
|
||||||
if (key in match) {
|
|
||||||
let keyMatch = match[key];
|
|
||||||
if (!Array.isArray(keyMatch))
|
|
||||||
keyMatch = [keyMatch];
|
|
||||||
expected = keyMatch;
|
|
||||||
} else if (!strict) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const values = params.getAll(key);
|
|
||||||
if (strict && values.length !== expected.length)
|
|
||||||
return false;
|
return false;
|
||||||
for (const v of values) {
|
if (typeof value !== 'string') {
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const e of expected) {
|
for (const v of value)
|
||||||
if (helper.stringMatches(v, e, name + '.' + key)) {
|
found = found || helper.stringMatches(v, match[key], name + '.' + key);
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
if (!found)
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!helper.stringMatches(value, match[key], name + '.' + key))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static urlMatches(urlString: string, match: types.URLMatch): boolean {
|
static urlMatches(urlString: string, match: types.URLMatch): boolean {
|
||||||
let url;
|
let url: URL.UrlWithParsedQuery;
|
||||||
try {
|
try {
|
||||||
url = new URL(urlString);
|
url = URL.parse(urlString, true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return urlString === match.url &&
|
return urlString === match.url && !match.queryParams;
|
||||||
match.hash === undefined &&
|
|
||||||
match.host === undefined &&
|
|
||||||
match.hostname === undefined &&
|
|
||||||
match.origin === undefined &&
|
|
||||||
match.password === undefined &&
|
|
||||||
match.pathname === undefined &&
|
|
||||||
match.port === undefined &&
|
|
||||||
match.protocol === undefined &&
|
|
||||||
match.search === undefined &&
|
|
||||||
match.searchParams === undefined &&
|
|
||||||
match.username === undefined;
|
|
||||||
}
|
}
|
||||||
if (match.url !== undefined && !helper.stringMatches(urlString, match.url, 'url'))
|
if (match.url !== undefined && !helper.stringMatches(urlString, match.url, 'url'))
|
||||||
return false;
|
return false;
|
||||||
if (match.hash !== undefined && !helper.stringMatches(url.hash, match.hash, 'hash'))
|
if (match.queryParams && !helper.queryParamsMatch(url.query, match.queryParams, 'queryParams'))
|
||||||
return false;
|
|
||||||
if (match.host !== undefined && !helper.stringMatches(url.host, match.host, 'host'))
|
|
||||||
return false;
|
|
||||||
if (match.hostname !== undefined && !helper.stringMatches(url.hostname, match.hostname, 'hostname'))
|
|
||||||
return false;
|
|
||||||
if (match.origin !== undefined && !helper.stringMatches(url.origin, match.origin, 'origin'))
|
|
||||||
return false;
|
|
||||||
if (match.password !== undefined && !helper.stringMatches(url.password, match.password, 'password'))
|
|
||||||
return false;
|
|
||||||
if (match.pathname !== undefined && !helper.stringMatches(url.pathname, match.pathname, 'pathname'))
|
|
||||||
return false;
|
|
||||||
if (match.port !== undefined && !helper.stringMatches(url.port, match.port, 'port'))
|
|
||||||
return false;
|
|
||||||
if (match.protocol !== undefined && !helper.stringMatches(url.protocol, match.protocol, 'protocol'))
|
|
||||||
return false;
|
|
||||||
if (match.search !== undefined && !helper.stringMatches(url.search, match.search, 'search'))
|
|
||||||
return false;
|
|
||||||
if (match.username !== undefined && !helper.stringMatches(url.username, match.username, 'username'))
|
|
||||||
return false;
|
|
||||||
if (match.searchParams !== undefined && !helper.searchParamsMatch(url.searchParams, match.searchParams, !!match.strictSearchParams, 'searchParams'))
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/types.ts
15
src/types.ts
|
|
@ -50,19 +50,8 @@ export type Viewport = {
|
||||||
hasTouch?: boolean;
|
hasTouch?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SearchParamsMatch = { [key: string]: string | RegExp | (string | RegExp)[] };
|
export type URLQueryParamsMatch = { [key: string]: string | RegExp };
|
||||||
export type URLMatch = {
|
export type URLMatch = {
|
||||||
url?: string | RegExp,
|
url?: string | RegExp,
|
||||||
hash?: string | RegExp,
|
queryParams?: URLQueryParamsMatch,
|
||||||
host?: string | RegExp,
|
|
||||||
hostname?: string | RegExp,
|
|
||||||
origin?: string | RegExp,
|
|
||||||
password?: string | RegExp,
|
|
||||||
pathname?: string | RegExp,
|
|
||||||
port?: string | RegExp,
|
|
||||||
protocol?: string | RegExp,
|
|
||||||
search?: string | RegExp,
|
|
||||||
strictSearchParams?: boolean,
|
|
||||||
searchParams?: SearchParamsMatch,
|
|
||||||
username?: string | RegExp,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -668,35 +668,31 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
let response1 = null;
|
let response1 = null;
|
||||||
const response1Promise = page.waitForNavigation({ url: /one-style\.html/ }).then(response => response1 = response);
|
const response1Promise = page.waitForNavigation({ url: /one-style\.html/ }).then(response => response1 = response);
|
||||||
let response2 = null;
|
let response2 = null;
|
||||||
const response2Promise = page.waitForNavigation({ pathname: '/frame.html' }).then(response => response2 = response);
|
const response2Promise = page.waitForNavigation({ queryParams: { 'foo': 'bar' }}).then(response => response2 = response);
|
||||||
let response3 = null;
|
|
||||||
const response3Promise = page.waitForNavigation({ searchParams: { 'foo': 'bar' }, strictSearchParams: true }).then(response => response3 = response);
|
|
||||||
expect(response1).toBe(null);
|
expect(response1).toBe(null);
|
||||||
expect(response2).toBe(null);
|
expect(response2).toBe(null);
|
||||||
expect(response3).toBe(null);
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
expect(response1).toBe(null);
|
expect(response1).toBe(null);
|
||||||
expect(response2).toBe(null);
|
expect(response2).toBe(null);
|
||||||
expect(response3).toBe(null);
|
|
||||||
await page.goto(server.PREFIX + '/frame.html');
|
await page.goto(server.PREFIX + '/frame.html');
|
||||||
expect(response1).toBe(null);
|
expect(response1).toBe(null);
|
||||||
await response2Promise;
|
expect(response2).toBe(null);
|
||||||
expect(response2).not.toBe(null);
|
|
||||||
expect(response3).toBe(null);
|
|
||||||
await page.goto(server.PREFIX + '/one-style.html');
|
await page.goto(server.PREFIX + '/one-style.html');
|
||||||
await response1Promise;
|
await response1Promise;
|
||||||
expect(response1).not.toBe(null);
|
expect(response1).not.toBe(null);
|
||||||
expect(response2).not.toBe(null);
|
expect(response2).toBe(null);
|
||||||
expect(response3).toBe(null);
|
|
||||||
await page.goto(server.PREFIX + '/frame.html?foo=bar');
|
await page.goto(server.PREFIX + '/frame.html?foo=bar');
|
||||||
await response3Promise;
|
await response2Promise;
|
||||||
expect(response1).not.toBe(null);
|
expect(response1).not.toBe(null);
|
||||||
expect(response2).not.toBe(null);
|
expect(response2).not.toBe(null);
|
||||||
expect(response3).not.toBe(null);
|
|
||||||
await page.goto(server.PREFIX + '/empty.html');
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
expect(response1.url()).toBe(server.PREFIX + '/one-style.html');
|
expect(response1.url()).toBe(server.PREFIX + '/one-style.html');
|
||||||
expect(response2.url()).toBe(server.PREFIX + '/frame.html');
|
expect(response2.url()).toBe(server.PREFIX + '/frame.html?foo=bar');
|
||||||
expect(response3.url()).toBe(server.PREFIX + '/frame.html?foo=bar');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -344,47 +344,10 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
|
||||||
]);
|
]);
|
||||||
expect(request.url()).toBe(server.PREFIX + '/digits/1.png');
|
expect(request.url()).toBe(server.PREFIX + '/digits/1.png');
|
||||||
});
|
});
|
||||||
it('should work with pathname match', async({page, server}) => {
|
it('should work with query params match', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const [request] = await Promise.all([
|
const [request] = await Promise.all([
|
||||||
page.waitForRequest({ pathname: '/digits/2.png' }),
|
page.waitForRequest({ queryParams: { 'foo': /baz|bar/ }, url: /\.png/ }),
|
||||||
page.evaluate(() => {
|
|
||||||
fetch('/digits/1.png');
|
|
||||||
fetch('/digits/2.png');
|
|
||||||
fetch('/digits/3.png');
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
expect(request.url()).toBe(server.PREFIX + '/digits/2.png');
|
|
||||||
});
|
|
||||||
it('should work with multiple matches', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
page.waitForRequest({ pathname: '/digits/2.png', url: /\d\.png/, port: String(server.PORT) }),
|
|
||||||
page.evaluate(() => {
|
|
||||||
fetch('/digits/1.png');
|
|
||||||
fetch('/digits/2.png');
|
|
||||||
fetch('/digits/3.png');
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
expect(request.url()).toBe(server.PREFIX + '/digits/2.png');
|
|
||||||
});
|
|
||||||
it('should work with strict search params match', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
page.waitForRequest({ searchParams: { 'foo': [/^baz$/, 'bar'], 'bar': 'foo' }, strictSearchParams: true }),
|
|
||||||
page.evaluate(() => {
|
|
||||||
fetch('/digits/2.png?foo=bar&foo=baz&bar=foo&key=value');
|
|
||||||
fetch('/digits/1.png?foo=bar&bar=foo');
|
|
||||||
fetch('/digits/4.png?foo=bar&bar=foo&foo=baz');
|
|
||||||
fetch('/digits/3.png?bar=foo');
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
expect(request.url()).toBe(server.PREFIX + '/digits/4.png?foo=bar&bar=foo&foo=baz');
|
|
||||||
});
|
|
||||||
it('should work with relaxed search params match', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
page.waitForRequest({ searchParams: { 'foo': ['bar', /^baz$/], 'bar': 'foo' }, url: /\.png/ }),
|
|
||||||
page.evaluate(() => {
|
page.evaluate(() => {
|
||||||
fetch('/digits/1.png?key=value&foo=something');
|
fetch('/digits/1.png?key=value&foo=something');
|
||||||
fetch('/digits/2.png?foo=baz');
|
fetch('/digits/2.png?foo=baz');
|
||||||
|
|
@ -402,15 +365,15 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
|
||||||
]);
|
]);
|
||||||
expect(error.message).toBe('url match field "url" must be a string or a RegExp, got object');
|
expect(error.message).toBe('url match field "url" must be a string or a RegExp, got object');
|
||||||
});
|
});
|
||||||
it('should throw for incorrect searchParams match', async({page, server}) => {
|
it('should throw for incorrect queryParams match', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const [error] = await Promise.all([
|
const [error] = await Promise.all([
|
||||||
page.waitForRequest({ searchParams: { 'foo': 123 }, url: /\.png/ }).catch(e => e),
|
page.waitForRequest({ queryParams: { 'foo': 123 }, url: /\.png/ }).catch(e => e),
|
||||||
page.evaluate(() => {
|
page.evaluate(() => {
|
||||||
fetch('/digits/1.png?foo=bar');
|
fetch('/digits/1.png?foo=bar');
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
expect(error.message).toBe('url match field "searchParams.foo" must be a string or a RegExp, got number');
|
expect(error.message).toBe('url match field "queryParams.foo" must be a string or a RegExp, got number');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -462,18 +425,6 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
|
||||||
]);
|
]);
|
||||||
expect(response.url()).toBe(server.PREFIX + '/digits/2.png');
|
expect(response.url()).toBe(server.PREFIX + '/digits/2.png');
|
||||||
});
|
});
|
||||||
it('should work with multiple matches', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
const [response] = await Promise.all([
|
|
||||||
page.waitForResponse({ pathname: '/digits/2.png', url: /\d\.png/, port: String(server.PORT) }),
|
|
||||||
page.evaluate(() => {
|
|
||||||
fetch('/digits/1.png');
|
|
||||||
fetch('/digits/2.png');
|
|
||||||
fetch('/digits/3.png');
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
expect(response.url()).toBe(server.PREFIX + '/digits/2.png');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.exposeFunction', function() {
|
describe('Page.exposeFunction', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue