fix(webkit): wait for main response on subresource-free goto (#216)

This commit is contained in:
Pavel Feldman 2019-12-11 16:19:37 -08:00 committed by Dmitry Gozman
parent 78847c2f52
commit 6440323003
9 changed files with 52 additions and 40 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -680,8 +680,8 @@ export class LifecycleWatcher {
this._checkLifecycleComplete();
}
navigationResponse(): network.Response | null {
return this._navigationRequest ? this._navigationRequest.response() : null;
navigationResponse(): Promise<network.Response | null> {
return this._navigationRequest ? this._navigationRequest._waitForFinishedResponse() : null;
}
private _createTimeoutPromise(timeout: number): Promise<Error | null> {

View File

@ -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<Response>;
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<Response> {
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<Buffer>;
export class Response {
private _request: Request;
private _contentPromise: Promise<Buffer> | null = null;
private _bodyLoadedPromise: Promise<Error | null>;
private _bodyLoadedPromiseFulfill: any;
_requestFinishedPromise: Promise<Error | null>;
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<Buffer> {
if (!this._contentPromise) {
this._contentPromise = this._bodyLoadedPromise.then(async error => {
this._contentPromise = this._requestFinishedPromise.then(async error => {
if (error)
throw error;
return this._getResponseBodyCallback();

View File

@ -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);

View File

@ -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,

View File

@ -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);
});
});

View File

@ -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);

View File

@ -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(`