diff --git a/packages/playwright-core/src/utils/isomorphic/urlMatch.ts b/packages/playwright-core/src/utils/isomorphic/urlMatch.ts index 1d3bd011dc..55fc6c7d72 100644 --- a/packages/playwright-core/src/utils/isomorphic/urlMatch.ts +++ b/packages/playwright-core/src/utils/isomorphic/urlMatch.ts @@ -101,7 +101,35 @@ export function urlMatches(baseURL: string | undefined, urlString: string, match // Allow http(s) baseURL to match ws(s) urls. if (baseURL && /^https?:\/\//.test(baseURL) && /^wss?:\/\//.test(urlString)) baseURL = baseURL.replace(/^http/, 'ws'); - match = constructURLBasedOnBaseURL(baseURL, match); + + const tokenMap = new Map(); + function mapToken(original: string, replacement: string) { + tokenMap.set(replacement, original); + return replacement; + } + // Glob symbols may be escaped in the URL and some of them such as ? affect resolution, + // so we replace them with safe components first. + const relativePath = match.split('/').map((token, index) => { + if (token === '.' || token === '..' || token === '') + return token; + // Handle special case of http*:// + if (index === 0 && token.endsWith(':')) { + return mapToken(token, 'http:'); + } else { + const questionIndex = token.indexOf('?'); + if (questionIndex === -1) + return mapToken(token, `$_${index}_$`); + if (questionIndex === 0) + return mapToken(token, `?$_${index}_$`); + const newPrefix = mapToken(token.substring(0, questionIndex), `$_${index}_$`); + const newSuffix = mapToken(token.substring(questionIndex), `?$_${index}_$`); + return newPrefix + newSuffix; + } + }).join('/'); + let resolved = constructURLBasedOnBaseURL(baseURL, relativePath); + for (const [token, original] of tokenMap) + resolved = resolved.replace(token, original); + match = resolved; } if (isString(match)) match = globToRegex(match); diff --git a/tests/page/interception.spec.ts b/tests/page/interception.spec.ts index d3443f9015..c93d7c3233 100644 --- a/tests/page/interception.spec.ts +++ b/tests/page/interception.spec.ts @@ -16,7 +16,7 @@ */ import { test as it, expect } from './pageTest'; -import { globToRegex } from '../../packages/playwright-core/lib/utils/isomorphic/urlMatch'; +import { globToRegex, urlMatches } from '../../packages/playwright-core/lib/utils/isomorphic/urlMatch'; import vm from 'vm'; it('should work with navigation @smoke', async ({ page, server }) => { @@ -105,6 +105,30 @@ it('should work with glob', async () => { expect(globToRegex('\\[')).toEqual(/^\[$/); expect(globToRegex('[a-z]')).toEqual(/^[a-z]$/); expect(globToRegex('$^+.\\*()|\\?\\{\\}\\[\\]')).toEqual(/^\$\^\+\.\*\(\)\|\?\{\}\[\]$/); + + expect(urlMatches(undefined, 'http://playwright.dev/', 'http://playwright.dev')).toBeTruthy(); + expect(urlMatches(undefined, 'http://playwright.dev/?a=b', 'http://playwright.dev?a=b')).toBeTruthy(); + expect(urlMatches(undefined, 'http://playwright.dev/', 'h*://playwright.dev')).toBeTruthy(); + expect(urlMatches(undefined, 'http://api.playwright.dev/?x=y', 'http://*.playwright.dev?x=y')).toBeTruthy(); + expect(urlMatches(undefined, 'http://playwright.dev/foo/bar', '**/foo/**')).toBeTruthy(); + expect(urlMatches('http://playwright.dev/foo/', 'http://playwright.dev/foo/bar?x=y', './bar?x=y')).toBeTruthy(); + + // This is not supported, we treat ? as a query separator. + expect(urlMatches(undefined, 'http://playwright.dev/', 'http://playwright.?ev')).toBeFalsy(); +}); + +it('should intercept by glob', async function({ page, server, isAndroid }) { + it.skip(isAndroid); + + await page.goto(server.EMPTY_PAGE); + await page.route('http://localhos**/?oo', async route => { + await route.fulfill({ + status: 200, + body: 'intercepted', + }); + }); + const result = await page.evaluate(url => fetch(url).then(r => r.text()), server.PREFIX + '/foo'); + expect(result).toBe('intercepted'); }); it('should intercept network activity from worker', async function({ page, server, isAndroid }) {