From fdfec8eb2a4e476004ff81fc8be69e03b7ee30ae Mon Sep 17 00:00:00 2001 From: aesyondu <57334943+aesyondu@users.noreply.github.com> Date: Tue, 25 Feb 2020 10:32:17 +0800 Subject: [PATCH] fix(platform) instanceof bug between execution contexts of RegExp object (#1048) First encountered at jest-playwright repo: https://github.com/mmarkelov/jest-playwright/issues/38 Solution based on: https://stackoverflow.com/questions/4339288/typeof-for-regexp#comment4724685_4339350 --- src/helper.ts | 4 ++++ src/page.ts | 4 ++-- src/platform.ts | 2 +- test/interception.spec.js | 23 +++++++++++++++++++++++ test/page.spec.js | 14 ++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/helper.ts b/src/helper.ts index 0c79075566..0f421375df 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -98,6 +98,10 @@ class Helper { return typeof obj === 'number' || obj instanceof Number; } + static isRegExp(obj: any): obj is RegExp { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]'; + } + static isObject(obj: any): obj is NonNullable { return typeof obj === 'object' && obj !== null; } diff --git a/src/page.ts b/src/page.ts index 9fcb31a0b0..572b2a9ece 100644 --- a/src/page.ts +++ b/src/page.ts @@ -358,7 +358,7 @@ export class Page extends platform.EventEmitter { async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise { const { timeout = this._timeoutSettings.timeout() } = options; return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => { - if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp) + if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate)) return platform.urlMatches(request.url(), urlOrPredicate); return urlOrPredicate(request); }, timeout, this._disconnectedPromise); @@ -367,7 +367,7 @@ export class Page extends platform.EventEmitter { async waitForResponse(urlOrPredicate: string | RegExp | ((r: network.Response) => boolean), options: types.TimeoutOptions = {}): Promise { const { timeout = this._timeoutSettings.timeout() } = options; return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => { - if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp) + if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate)) return platform.urlMatches(response.url(), urlOrPredicate); return urlOrPredicate(response); }, timeout, this._disconnectedPromise); diff --git a/src/platform.ts b/src/platform.ts index 7015b89519..ac313f5c8e 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -222,7 +222,7 @@ export function urlMatches(urlString: string, match: types.URLMatch | undefined) return true; if (helper.isString(match)) match = helper.globToRegex(match); - if (match instanceof RegExp) + if (helper.isRegExp(match)) return match.test(urlString); if (typeof match === 'string' && match === urlString) return true; diff --git a/test/interception.spec.js b/test/interception.spec.js index 7bdf9ca815..90fa5e33b5 100644 --- a/test/interception.spec.js +++ b/test/interception.spec.js @@ -19,6 +19,7 @@ const fs = require('fs'); const path = require('path'); const { helper } = require('../lib/helper'); const utils = require('./utils'); +const vm = require('vm'); /** * @type {PageTestSuite} @@ -618,6 +619,28 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p expect(helper.globToRegex('**/*.{png,jpg,jpeg}').test('https://localhost:8080/c.css')).toBeFalsy(); }); }); + + describe('regexp', function() { + it('should work with regular expression passed from a different context', async({page, server}) => { + const ctx = vm.createContext(); + const regexp = vm.runInContext('new RegExp("empty\\.html")', ctx); + + await page.route(regexp, request => { + expect(request.url()).toContain('empty.html'); + expect(request.headers()['user-agent']).toBeTruthy(); + expect(request.method()).toBe('GET'); + expect(request.postData()).toBe(undefined); + expect(request.isNavigationRequest()).toBe(true); + expect(request.resourceType()).toBe('document'); + expect(request.frame() === page.mainFrame()).toBe(true); + expect(request.frame().url()).toBe('about:blank'); + request.continue(); + }); + + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok()).toBe(true); + }); + }); }; /** diff --git a/test/page.spec.js b/test/page.spec.js index d13f45f902..5036a9061a 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -18,6 +18,7 @@ const fs = require('fs'); const path = require('path'); const utils = require('./utils'); const {waitEvent} = utils; +const vm = require('vm'); /** * @type {PageTestSuite} @@ -309,6 +310,19 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF ]); expect(request.url()).toBe(server.PREFIX + '/digits/1.png'); }); + it('should work with url match regular expression from a different context', async({page, server}) => { + const ctx = vm.createContext(); + const regexp = vm.runInContext('new RegExp(/digits\\/\\d\\.png/)', ctx); + + await page.goto(server.EMPTY_PAGE); + const [request] = await Promise.all([ + page.waitForRequest(regexp), + page.evaluate(() => { + fetch('/digits/1.png'); + }) + ]); + expect(request.url()).toBe(server.PREFIX + '/digits/1.png'); + }); }); describe('Page.waitForResponse', function() {