diff --git a/src/chromium/NetworkManager.ts b/src/chromium/NetworkManager.ts index af16f35215..3da800b001 100644 --- a/src/chromium/NetworkManager.ts +++ b/src/chromium/NetworkManager.ts @@ -215,7 +215,7 @@ export class NetworkManager extends EventEmitter { _handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) { const response = this._createResponse(request, responsePayload); request.request._redirectChain.push(request.request); - response._bodyLoaded(new Error('Response body is unavailable for redirect responses')); + response._requestFinished(new Error('Response body is unavailable for redirect responses')); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.Response, response); @@ -241,7 +241,7 @@ export class NetworkManager extends EventEmitter { // Under certain conditions we never get the Network.responseReceived // event from protocol. @see https://crbug.com/883475 if (request.request.response()) - request.request.response()._bodyLoaded(); + request.request.response()._requestFinished(); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.RequestFinished, request.request); @@ -256,7 +256,7 @@ export class NetworkManager extends EventEmitter { request.request._setFailureText(event.errorText); const response = request.request.response(); if (response) - response._bodyLoaded(); + response._requestFinished(); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.RequestFailed, request.request); diff --git a/src/firefox/NetworkManager.ts b/src/firefox/NetworkManager.ts index c87264dbc8..09a56ba4f7 100644 --- a/src/firefox/NetworkManager.ts +++ b/src/firefox/NetworkManager.ts @@ -111,10 +111,10 @@ export class NetworkManager extends EventEmitter { // Keep redirected requests in the map for future reference in redirectChain. const isRedirected = response.status() >= 300 && response.status() <= 399; if (isRedirected) { - response._bodyLoaded(new Error('Response body is unavailable for redirect responses')); + response._requestFinished(new Error('Response body is unavailable for redirect responses')); } else { this._requests.delete(request._id); - response._bodyLoaded(); + response._requestFinished(); } this.emit(NetworkManagerEvents.RequestFinished, request.request); } @@ -125,7 +125,7 @@ export class NetworkManager extends EventEmitter { return; this._requests.delete(request._id); if (request.request.response()) - request.request.response()._bodyLoaded(); + request.request.response()._requestFinished(); request.request._setFailureText(event.errorCode); this.emit(NetworkManagerEvents.RequestFailed, request.request); } diff --git a/src/frames.ts b/src/frames.ts index 7624253452..61bccfc493 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -680,8 +680,8 @@ export class LifecycleWatcher { this._checkLifecycleComplete(); } - navigationResponse(): network.Response | null { - return this._navigationRequest ? this._navigationRequest.response() : null; + navigationResponse(): Promise { + return this._navigationRequest ? this._navigationRequest._waitForFinishedResponse() : null; } private _createTimeoutPromise(timeout: number): Promise { diff --git a/src/network.ts b/src/network.ts index d8ec7c4e2d..427ee0155e 100644 --- a/src/network.ts +++ b/src/network.ts @@ -70,7 +70,7 @@ export function rewriteCookies(cookies: SetNetworkCookieParam[]): SetNetworkCook export type Headers = { [key: string]: string }; export class Request { - _response: Response | null = null; + private _response: Response | null = null; _redirectChain: Request[]; private _isNavigationRequest: boolean; private _failureText: string | null = null; @@ -80,6 +80,8 @@ export class Request { private _postData: string; private _headers: Headers; private _frame: frames.Frame; + private _waitForResponsePromise: Promise; + private _waitForResponsePromiseCallback: (value?: Response) => void; constructor(frame: frames.Frame | null, redirectChain: Request[], isNavigationRequest: boolean, url: string, resourceType: string, method: string, postData: string, headers: Headers) { @@ -91,6 +93,7 @@ export class Request { this._method = method; this._postData = postData; this._headers = headers; + this._waitForResponsePromise = new Promise(f => this._waitForResponsePromiseCallback = f); } _setFailureText(failureText: string) { @@ -121,6 +124,17 @@ export class Request { return this._response; } + async _waitForFinishedResponse(): Promise { + const response = await this._waitForResponsePromise; + await response._requestFinishedPromise; + return response; + } + + _setResponse(response: Response) { + this._response = response; + this._waitForResponsePromiseCallback(response); + } + frame(): frames.Frame | null { return this._frame; } @@ -152,8 +166,8 @@ type GetResponseBodyCallback = () => Promise; export class Response { private _request: Request; private _contentPromise: Promise | null = null; - private _bodyLoadedPromise: Promise; - private _bodyLoadedPromiseFulfill: any; + _requestFinishedPromise: Promise; + private _requestFinishedPromiseCallback: any; private _remoteAddress: RemoteAddress; private _status: number; private _statusText: string; @@ -163,20 +177,20 @@ export class Response { constructor(request: Request, status: number, statusText: string, headers: Headers, remoteAddress: RemoteAddress, getResponseBodyCallback: GetResponseBodyCallback) { this._request = request; - this._request._response = this; + this._request._setResponse(this); this._status = status; this._statusText = statusText; this._url = request.url(); this._headers = headers; this._remoteAddress = remoteAddress; this._getResponseBodyCallback = getResponseBodyCallback; - this._bodyLoadedPromise = new Promise(fulfill => { - this._bodyLoadedPromiseFulfill = fulfill; + this._requestFinishedPromise = new Promise(f => { + this._requestFinishedPromiseCallback = f; }); } - _bodyLoaded(error?: Error) { - this._bodyLoadedPromiseFulfill.call(null, error); + _requestFinished(error?: Error) { + this._requestFinishedPromiseCallback.call(null, error); } remoteAddress(): RemoteAddress { @@ -205,7 +219,7 @@ export class Response { buffer(): Promise { if (!this._contentPromise) { - this._contentPromise = this._bodyLoadedPromise.then(async error => { + this._contentPromise = this._requestFinishedPromise.then(async error => { if (error) throw error; return this._getResponseBodyCallback(); diff --git a/src/webkit/NetworkManager.ts b/src/webkit/NetworkManager.ts index 2cebc327b6..c1a984cae4 100644 --- a/src/webkit/NetworkManager.ts +++ b/src/webkit/NetworkManager.ts @@ -115,7 +115,7 @@ export class NetworkManager extends EventEmitter { _handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) { const response = this._createResponse(request, responsePayload); request.request._redirectChain.push(request.request); - response._bodyLoaded(new Error('Response body is unavailable for redirect responses')); + response._requestFinished(new Error('Response body is unavailable for redirect responses')); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.Response, response); @@ -141,7 +141,7 @@ export class NetworkManager extends EventEmitter { // Under certain conditions we never get the Network.responseReceived // event from protocol. @see https://crbug.com/883475 if (request.request.response()) - request.request.response()._bodyLoaded(); + request.request.response()._requestFinished(); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.RequestFinished, request.request); @@ -156,7 +156,7 @@ export class NetworkManager extends EventEmitter { request.request._setFailureText(event.errorText); const response = request.request.response(); if (response) - response._bodyLoaded(); + response._requestFinished(); this._requestIdToRequest.delete(request._requestId); this._attemptedAuthentications.delete(request._interceptionId); this.emit(NetworkManagerEvents.RequestFailed, request.request); diff --git a/test/launcher.spec.js b/test/launcher.spec.js index 56b56c3d2e..2a1c2e81b7 100644 --- a/test/launcher.spec.js +++ b/test/launcher.spec.js @@ -23,7 +23,7 @@ const mkdtempAsync = helper.promisify(fs.mkdtemp); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const utils = require('./utils'); -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, playwright}) { +module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, playwright, WEBKIT}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; @@ -45,7 +45,8 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await playwright.launch(options).catch(e => waitError = e); expect(waitError.message).toContain('Failed to launch'); }); - it('should set the default viewport', async() => { + // Fails on GTK due to async setViewport. + it.skip(WEBKIT)('should set the default viewport', async() => { const options = Object.assign({}, defaultBrowserOptions, { defaultViewport: { width: 456, diff --git a/test/network.spec.js b/test/network.spec.js index 0980d699fc..f9572d4d43 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -234,14 +234,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(remoteAddress.port).toBe(server.PORT); }); - // FIXME: requires request interception. - it.skip(WEBKIT)('Page.Events.RequestFailed', async({page, server}) => { - await page.interception.enable(); - page.on('request', request => { - if (request.url().endsWith('css')) - page.interception.abort(request); - else - page.interception.continue(request); + it.skip(FFOX)('Page.Events.RequestFailed', async({page, server}) => { + server.setRoute('/one-style.css', (req, res) => { + req.socket.write('deadbeef'); + req.socket.end(); }); const failedRequests = []; page.on('requestfailed', request => failedRequests.push(request)); @@ -250,14 +246,15 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); - if (CHROME || WEBKIT) - expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); + if (CHROME) + expect(failedRequests[0].failure().errorText).toBe('net::ERR_INVALID_HTTP_RESPONSE'); + else if (WEBKIT) + expect(failedRequests[0].failure().errorText).toBe('Message Corrupt'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); - // FIXME: WebKit requestfinished comes after goto. - it.skip(WEBKIT)('Page.Events.RequestFinished', async({page, server}) => { + it('Page.Events.RequestFinished', async({page, server}) => { const requests = []; page.on('requestfinished', request => requests.push(request)); await page.goto(server.EMPTY_PAGE); @@ -267,7 +264,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(requests[0].frame() === page.mainFrame()).toBe(true); expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); }); - it.skip(WEBKIT)('should fire events in proper order', async({page, server}) => { + it('should fire events in proper order', async({page, server}) => { const events = []; page.on('request', request => events.push('request')); page.on('response', response => events.push('response')); @@ -275,7 +272,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { await page.goto(server.EMPTY_PAGE); expect(events).toEqual(['request', 'response', 'requestfinished']); }); - it.skip(WEBKIT)('should support redirects', async({page, server}) => { + it('should support redirects', async({page, server}) => { const events = []; page.on('request', request => events.push(`${request.method()} ${request.url()}`)); page.on('response', response => events.push(`${response.status()} ${response.url()}`)); @@ -297,7 +294,6 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { const redirectChain = response.request().redirectChain(); expect(redirectChain.length).toBe(1); expect(redirectChain[0].url()).toContain('/foo.html'); - expect(redirectChain[0].response().remoteAddress().port).toBe(server.PORT); }); }); diff --git a/test/page.spec.js b/test/page.spec.js index e2c4542a64..c609239ba8 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -118,7 +118,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF }); }); - describe.skip(WEBKIT)('Page.Events.Popup', function() { + describe('Page.Events.Popup', function() { it('should work', async({page}) => { const [popup] = await Promise.all([ new Promise(x => page.once('popup', x)), @@ -719,7 +719,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF expect(await page.evaluate(() => __injected)).toBe(42); }); - it('should include sourceURL when path is provided', async({page, server}) => { + (CHROME || FFOX) && it('should include sourceURL when path is provided', async({page, server}) => { await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); const result = await page.evaluate(() => __injectedError.stack); diff --git a/test/screenshot.spec.js b/test/screenshot.spec.js index fc8a6a607c..c86a4c3069 100644 --- a/test/screenshot.spec.js +++ b/test/screenshot.spec.js @@ -200,7 +200,8 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 }); }); - it('should capture full element when larger than viewport', async({page, server}) => { + // Fails on GTK due to async setViewport. + it.skip(WEBKIT)('should capture full element when larger than viewport', async({page, server}) => { await page.setViewport({width: 500, height: 500}); await page.setContent(`