From 55ad47c047b552433492878146f26d96f596bda3 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 19 Dec 2019 22:55:29 -0800 Subject: [PATCH] feat(urlMatch): simplify the url match --- src/helper.ts | 73 +++++++++-------------------------------- src/types.ts | 15 ++------- test/navigation.spec.js | 24 ++++++-------- test/page.spec.js | 59 +++------------------------------ 4 files changed, 32 insertions(+), 139 deletions(-) diff --git a/src/helper.ts b/src/helper.ts index 5b31ad862c..51d8052cf7 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -17,6 +17,7 @@ import * as debug from 'debug'; import * as types from './types'; +import * as URL from 'url'; import { TimeoutError } from './errors'; 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}`); } - 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) throw new Error(`url match field "${name}" must be an object, got ${typeof match}`); - const keys = new Set((params as any).keys()) as Set; - if (strict && keys.size !== Object.keys(match).length) - return false; - 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) + for (const key of Object.keys(match)) { + const value = params[key]; + if (!value) return false; - for (const v of values) { + if (typeof value !== 'string') { let found = false; - for (const e of expected) { - if (helper.stringMatches(v, e, name + '.' + key)) { - found = true; - break; - } - } + for (const v of value) + found = found || helper.stringMatches(v, match[key], name + '.' + key); if (!found) return false; + } else { + if (!helper.stringMatches(value, match[key], name + '.' + key)) + return false; } } return true; } static urlMatches(urlString: string, match: types.URLMatch): boolean { - let url; + let url: URL.UrlWithParsedQuery; try { - url = new URL(urlString); + url = URL.parse(urlString, true); } catch (e) { - return urlString === match.url && - 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; + return urlString === match.url && !match.queryParams; } if (match.url !== undefined && !helper.stringMatches(urlString, match.url, 'url')) return false; - if (match.hash !== undefined && !helper.stringMatches(url.hash, match.hash, 'hash')) - 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')) + if (match.queryParams && !helper.queryParamsMatch(url.query, match.queryParams, 'queryParams')) return false; return true; } diff --git a/src/types.ts b/src/types.ts index 930ca34c42..dbbd429c34 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,19 +50,8 @@ export type Viewport = { hasTouch?: boolean; }; -export type SearchParamsMatch = { [key: string]: string | RegExp | (string | RegExp)[] }; +export type URLQueryParamsMatch = { [key: string]: string | RegExp }; export type URLMatch = { url?: string | RegExp, - hash?: string | RegExp, - 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, + queryParams?: URLQueryParamsMatch, }; diff --git a/test/navigation.spec.js b/test/navigation.spec.js index ab7070f9ef..d811461c5e 100644 --- a/test/navigation.spec.js +++ b/test/navigation.spec.js @@ -668,35 +668,31 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME let response1 = null; const response1Promise = page.waitForNavigation({ url: /one-style\.html/ }).then(response => response1 = response); let response2 = null; - const response2Promise = page.waitForNavigation({ pathname: '/frame.html' }).then(response => response2 = response); - let response3 = null; - const response3Promise = page.waitForNavigation({ searchParams: { 'foo': 'bar' }, strictSearchParams: true }).then(response => response3 = response); + const response2Promise = page.waitForNavigation({ queryParams: { 'foo': 'bar' }}).then(response => response2 = response); expect(response1).toBe(null); expect(response2).toBe(null); - expect(response3).toBe(null); + await page.goto(server.EMPTY_PAGE); expect(response1).toBe(null); expect(response2).toBe(null); - expect(response3).toBe(null); + await page.goto(server.PREFIX + '/frame.html'); expect(response1).toBe(null); - await response2Promise; - expect(response2).not.toBe(null); - expect(response3).toBe(null); + expect(response2).toBe(null); + await page.goto(server.PREFIX + '/one-style.html'); await response1Promise; expect(response1).not.toBe(null); - expect(response2).not.toBe(null); - expect(response3).toBe(null); + expect(response2).toBe(null); + await page.goto(server.PREFIX + '/frame.html?foo=bar'); - await response3Promise; + await response2Promise; expect(response1).not.toBe(null); expect(response2).not.toBe(null); - expect(response3).not.toBe(null); + await page.goto(server.PREFIX + '/empty.html'); expect(response1.url()).toBe(server.PREFIX + '/one-style.html'); - expect(response2.url()).toBe(server.PREFIX + '/frame.html'); - expect(response3.url()).toBe(server.PREFIX + '/frame.html?foo=bar'); + expect(response2.url()).toBe(server.PREFIX + '/frame.html?foo=bar'); }); }); diff --git a/test/page.spec.js b/test/page.spec.js index c963484672..bece2b2f96 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -344,47 +344,10 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF ]); 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); const [request] = await Promise.all([ - page.waitForRequest({ pathname: '/digits/2.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.waitForRequest({ queryParams: { 'foo': /baz|bar/ }, url: /\.png/ }), page.evaluate(() => { fetch('/digits/1.png?key=value&foo=something'); 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'); }); - 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); 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(() => { 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'); }); - 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() {