From 1890dc04ba8c86a5c636c34f7d75222ea7a1c6ac Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Tue, 12 Feb 2019 19:10:14 -0800 Subject: [PATCH] feat(firefox): Page.waitForRequest/Page.waitForResponse (#3989) Drive-by: refactor `Request.frame()` tests into a separate test suite. --- .../puppeteer-firefox/lib/NetworkManager.js | 4 +- experimental/puppeteer-firefox/lib/Page.js | 36 ++++++++++ experimental/puppeteer-firefox/lib/helper.js | 31 +++++++++ experimental/puppeteer-firefox/package.json | 2 +- test/navigation.spec.js | 4 +- test/network.spec.js | 66 ++++++++++++++----- test/page.spec.js | 4 +- test/utils.js | 4 ++ 8 files changed, 128 insertions(+), 23 deletions(-) diff --git a/experimental/puppeteer-firefox/lib/NetworkManager.js b/experimental/puppeteer-firefox/lib/NetworkManager.js index 7dd93205..66fa5952 100644 --- a/experimental/puppeteer-firefox/lib/NetworkManager.js +++ b/experimental/puppeteer-firefox/lib/NetworkManager.js @@ -19,11 +19,11 @@ class NetworkManager extends EventEmitter { } _onRequestWillBeSent(event) { - const frame = this._frameManager && event.frameId ? this._frameManager.frame(event.frameId) : null; + const redirected = event.redirectedFrom ? this._requests.get(event.redirectedFrom) : null; + const frame = redirected ? redirected.frame() : (this._frameManager && event.frameId ? this._frameManager.frame(event.frameId) : null); if (!frame) return; let redirectChain = []; - const redirected = event.redirectedFrom ? this._requests.get(event.redirectedFrom) : null; if (redirected) { redirectChain = redirected._redirectChain; redirectChain.push(redirected); diff --git a/experimental/puppeteer-firefox/lib/Page.js b/experimental/puppeteer-firefox/lib/Page.js index b489e1a1..5c15e5b6 100644 --- a/experimental/puppeteer-firefox/lib/Page.js +++ b/experimental/puppeteer-firefox/lib/Page.js @@ -94,6 +94,42 @@ class Page extends EventEmitter { this._viewport = null; } + /** + * @param {(string|Function)} urlOrPredicate + * @param {!{timeout?: number}=} options + * @return {!Promise} + */ + async waitForRequest(urlOrPredicate, options = {}) { + const { + timeout = this._timeoutSettings.timeout(), + } = options; + return helper.waitForEvent(this._networkManager, Events.NetworkManager.Request, request => { + if (helper.isString(urlOrPredicate)) + return (urlOrPredicate === request.url()); + if (typeof urlOrPredicate === 'function') + return !!(urlOrPredicate(request)); + return false; + }, timeout); + } + + /** + * @param {(string|Function)} urlOrPredicate + * @param {!{timeout?: number}=} options + * @return {!Promise} + */ + async waitForResponse(urlOrPredicate, options = {}) { + const { + timeout = this._timeoutSettings.timeout(), + } = options; + return helper.waitForEvent(this._networkManager, Events.NetworkManager.Response, response => { + if (helper.isString(urlOrPredicate)) + return (urlOrPredicate === response.url()); + if (typeof urlOrPredicate === 'function') + return !!(urlOrPredicate(response)); + return false; + }, timeout); + } + /** * @param {number} timeout */ diff --git a/experimental/puppeteer-firefox/lib/helper.js b/experimental/puppeteer-firefox/lib/helper.js index 6aeaba01..30d1b867 100644 --- a/experimental/puppeteer-firefox/lib/helper.js +++ b/experimental/puppeteer-firefox/lib/helper.js @@ -116,6 +116,37 @@ class Helper { listeners.splice(0, listeners.length); } + /** + * @param {!NodeJS.EventEmitter} emitter + * @param {(string|symbol)} eventName + * @param {function} predicate + * @return {!Promise} + */ + static waitForEvent(emitter, eventName, predicate, timeout) { + let eventTimeout, resolveCallback, rejectCallback; + const promise = new Promise((resolve, reject) => { + resolveCallback = resolve; + rejectCallback = reject; + }); + const listener = Helper.addEventListener(emitter, eventName, event => { + if (!predicate(event)) + return; + cleanup(); + resolveCallback(event); + }); + if (timeout) { + eventTimeout = setTimeout(() => { + cleanup(); + rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); + }, timeout); + } + function cleanup() { + Helper.removeEventListeners([listener]); + clearTimeout(eventTimeout); + } + return promise; + } + /** * @template T * @param {!Promise} promise diff --git a/experimental/puppeteer-firefox/package.json b/experimental/puppeteer-firefox/package.json index 7214ae56..c4d7eb2f 100644 --- a/experimental/puppeteer-firefox/package.json +++ b/experimental/puppeteer-firefox/package.json @@ -9,7 +9,7 @@ "node": ">=8.9.4" }, "puppeteer": { - "firefox_revision": "668e06245e539adf0d453b447401b8eb6e9acb44" + "firefox_revision": "ad27e01304952cb0ff0b2817016b0e9f31d7f8fa" }, "scripts": { "install": "node install.js", diff --git a/test/navigation.spec.js b/test/navigation.spec.js index 102cae23..550ea393 100644 --- a/test/navigation.spec.js +++ b/test/navigation.spec.js @@ -280,7 +280,7 @@ module.exports.addTests = function({testRunner, expect, Errors, CHROME}) { }); it_fails_ffox('should navigate to dataURL and fire dataURL requests', async({page, server}) => { const requests = []; - page.on('request', request => requests.push(request)); + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); const dataURL = 'data:text/html,
yo
'; const response = await page.goto(dataURL); expect(response.status()).toBe(200); @@ -289,7 +289,7 @@ module.exports.addTests = function({testRunner, expect, Errors, CHROME}) { }); it_fails_ffox('should navigate to URL with hash and fire requests without hash', async({page, server}) => { const requests = []; - page.on('request', request => requests.push(request)); + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); const response = await page.goto(server.EMPTY_PAGE + '#hash'); expect(response.status()).toBe(200); expect(response.url()).toBe(server.EMPTY_PAGE); diff --git a/test/network.spec.js b/test/network.spec.js index 4666e1b2..02a38d49 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -23,6 +23,56 @@ module.exports.addTests = function({testRunner, expect}) { const {it, fit, xit, it_fails_ffox} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; + describe('Page.Events.Request', function() { + it('should fire for navigation requests', async({page, server}) => { + const requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await page.goto(server.EMPTY_PAGE); + expect(requests.length).toBe(1); + }); + it('should fire for iframes', async({page, server}) => { + const requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + expect(requests.length).toBe(2); + }); + it('should fire for fetches', async({page, server}) => { + const requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + expect(requests.length).toBe(2); + }); + }); + + describe('Request.frame', function() { + it('should work for main frame navigation request', async({page, server}) => { + const requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await page.goto(server.EMPTY_PAGE); + expect(requests.length).toBe(1); + expect(requests[0].frame()).toBe(page.mainFrame()); + }); + it('should work for subframe navigation request', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + expect(requests.length).toBe(1); + expect(requests[0].frame()).toBe(page.frames()[1]); + }); + it('should work for fetch requests', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + let requests = []; + page.on('request', request => !utils.isFavicon(request) && requests.push(request)); + await page.evaluate(() => fetch('/digits/1.png')); + requests = requests.filter(request => !request.url().includes('favicon')); + expect(requests.length).toBe(1); + expect(requests[0].frame()).toBe(page.mainFrame()); + }); + }); + describe('Network Events', function() { it('Page.Events.Request', async({page, server}) => { const requests = []; @@ -632,22 +682,6 @@ module.exports.addTests = function({testRunner, expect}) { }); }); - describe('Page.Events.Request', function() { - it('should fire', async({page, server}) => { - const requests = []; - page.on('request', request => requests.push(request)); - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - expect(requests.length).toBe(2); - expect(requests[0].url()).toBe(server.EMPTY_PAGE); - expect(requests[0].frame() === page.mainFrame()).toBe(true); - expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); - expect(requests[1].url()).toBe(server.EMPTY_PAGE); - expect(requests[1].frame() === page.frames()[1]).toBe(true); - expect(requests[1].frame().url()).toBe(server.EMPTY_PAGE); - }); - }); - describe_fails_ffox('Page.setExtraHTTPHeaders', function() { it('should work', async({page, server}) => { await page.setExtraHTTPHeaders({ diff --git a/test/page.spec.js b/test/page.spec.js index f70d0701..9da38486 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -423,7 +423,7 @@ module.exports.addTests = function({testRunner, expect, headless, Errors, Device } }); - describe_fails_ffox('Page.waitForRequest', function() { + describe('Page.waitForRequest', function() { it('should work', async({page, server}) => { await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ @@ -473,7 +473,7 @@ module.exports.addTests = function({testRunner, expect, headless, Errors, Device }); }); - describe_fails_ffox('Page.waitForResponse', function() { + describe('Page.waitForResponse', function() { it('should work', async({page, server}) => { await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ diff --git a/test/utils.js b/test/utils.js index 041a930c..055e7a81 100644 --- a/test/utils.js +++ b/test/utils.js @@ -97,6 +97,10 @@ const utils = module.exports = { } }, + isFavicon: function(request) { + return request.url().includes('favicon.ico'); + }, + /** * @param {!Page} page * @param {string} frameId