Implement NetworkManager

This patch implements NetworkManager, which encapsulates all the
interaction with Network domain.

The NetworkManager also uses partial implementation of Request and
Response classes, defined in the Fetch API specification.

References #26
This commit is contained in:
Andrey Lushnikov 2017-06-28 23:09:28 -07:00
parent 4b0b81fd9b
commit a35a21dfaf
7 changed files with 464 additions and 224 deletions

View File

@ -13,7 +13,7 @@
- [page.close()](#pageclose)
- [page.evaluate(fun, args)](#pageevaluatefun-args)
- [page.evaluateOnInitialized(fun, args)](#pageevaluateoninitializedfun-args)
- [page.extraHTTPHeaders()](#pageextrahttpheaders)
- [page.httpHeaders()](#pagehttpheaders)
- [page.frames()](#pageframes)
- [page.injectFile(filePath)](#pageinjectfilefilepath)
- [page.mainFrame()](#pagemainframe)
@ -22,14 +22,14 @@
- [page.printToPDF(filePath[, options])](#pageprinttopdffilepath-options)
- [page.screenshot([options])](#pagescreenshotoptions)
- [page.setContent(html)](#pagesetcontenthtml)
- [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
- [page.setHTTPHeaders(headers)](#pagesethttpheadersheaders)
- [page.setInPageCallback(name, callback)](#pagesetinpagecallbackname-callback)
- [page.setRequestInterceptor()](#pagesetrequestinterceptor)
- [page.setUserAgentOverride(userAgent)](#pagesetuseragentoverrideuseragent)
- [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
- [page.setViewportSize(size)](#pagesetviewportsizesize)
- [page.title()](#pagetitle)
- [page.url()](#pageurl)
- [page.userAgentOverride()](#pageuseragentoverride)
- [page.userAgent()](#pageuseragent)
- [page.viewportSize()](#pageviewportsize)
+ [class: Dialog](#class-dialog)
- [dialog.accept()](#dialogaccept)
@ -125,7 +125,7 @@ Pages could be closed by `page.close()` method.
- `args` <[Array]<[string]>> Arguments to pass to `fun`
- returns: <[Promise]<[Object]>> Promise which resolves to function
#### page.extraHTTPHeaders()
#### page.httpHeaders()
- returns: <[Object]> Key-value set of additional http headers, which will be sent with every request.
@ -183,7 +183,7 @@ Pages could be closed by `page.close()` method.
- `html` <[string]> HTML markup to assign to the page.
- returns: <[Promise]> Promise which resolves when the content is successfully assigned.
#### page.setExtraHTTPHeaders(headers)
#### page.setHTTPHeaders(headers)
- `headers` <[Object]> Key-value set of additional http headers to be sent with every request.
- returns: <[Promise]> Promise which resolves when additional headers are installed
@ -196,7 +196,7 @@ Pages could be closed by `page.close()` method.
#### page.setRequestInterceptor()
#### page.setUserAgentOverride(userAgent)
#### page.setUserAgent(userAgent)
- `userAgent` <[string]> Specific user agent to use in this page
- returns: <[Promise]> Promise which resolves when the user agent is set.
@ -217,9 +217,9 @@ Pages could be closed by `page.close()` method.
- returns: <[Promise]<[string]>> Promise which resolves with the current page url.
#### page.userAgentOverride()
#### page.userAgent()
- returns: <[string]> Returns user agent override, if any.
- returns: <[string]> Returns user agent.
#### page.viewportSize()

View File

@ -20,7 +20,7 @@ let helper = require('./helper');
class FrameManager extends EventEmitter {
/**
* @param {!Connection} client
* @return {!FrameManager}
* @return {!Promise<!FrameManager>}
*/
static async create(client) {
let mainFramePayload = await client.send('Page.getResourceTree');

325
lib/NetworkManager.js Normal file
View File

@ -0,0 +1,325 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let EventEmitter = require('events');
class NetworkManager extends EventEmitter {
/**
* @param {!Connection} client
* @param {string} userAgent
*/
constructor(client, userAgent) {
super();
this._client = client;
this._requestInterceptor = null;
/* @type {!Map<string, !Request>} */
this._idToRequest = new Map();
/* @type {!Map<string, !Response>} */
this._idToResponse = new Map();
this._httpHeaders = {};
this._userAgent = userAgent;
this._client.on('Network.requestWillBeSent', this._onRequestWillBeSent.bind(this));
this._client.on('Network.requestIntercepted', this._onRequestIntercepted.bind(this));
this._client.on('Network.responseReceived', this._onResponseReceived.bind(this));
this._client.on('Network.loadingFinished', this._onLoadingFinished.bind(this));
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
}
/**
* @param {!Object} headers
* @return {!Promise}
*/
async setHTTPHeaders(headers) {
this._httpHeaders = {};
// Note: header names are case-insensitive.
for (let key of Object.keys(headers))
this._httpHeaders[key.toLowerCase()] = headers[key];
return this._client.send('Network.setExtraHTTPHeaders', { headers });
}
/**
* @return {!Object}
*/
httpHeaders() {
return Object.assign({}, this._httpHeaders);
}
/**
* @param {string} userAgent
* @return {!Promise}
*/
async setUserAgent(userAgent) {
this._userAgent = userAgent;
return this._client.send('Network.setUserAgentOverride', { userAgent });
}
/**
* @return {string}
*/
userAgent() {
return this._userAgent;
}
/**
* @param {?function(!InterceptedRequest)} interceptor
* @return {!Promise}
*/
async setRequestInterceptor(interceptor) {
this._requestInterceptor = interceptor;
await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor});
}
/**
* @param {!Object} event
*/
_onRequestIntercepted(event) {
let request = new InterceptedRequest(this._client, event.InterceptionId, event.request);
this._requestInterceptor(request);
}
/**
* @param {!Object} event
*/
_onRequestWillBeSent(event) {
let request = new Request(this, event.requestId, event.request);
this._idToRequest.set(event.requestId, request);
this.emit(NetworkManager.Events.Request, request);
}
/**
* @param {!Object} event
*/
_onRequestIntercepted(event) {
let request = new InterceptedRequest(this._client, event.InterceptionId, event.request);
this._requestInterceptor(request);
}
/**
* @param {!Object} event
*/
_onResponseReceived(event) {
let response = new Response(this, event.requestId, event.response);
this._idToResponse.set(event.requestId, response);
this.emit(NetworkManager.Events.Response, response);
}
/**
* @param {!Object} event
*/
_onLoadingFinished(event) {
let request = this._idToRequest.get(event.requestId);
this.emit(NetworkManager.Events.RequestFinished, request);
}
/**
* @param {!Object} event
*/
_onLoadingFailed(event) {
let request = this._idToRequest.get(event.requestId);
this.emit(NetworkManager.Events.RequestFailed, request);
}
}
class Headers {
/**
* @param {?Object} payload
* @return {!Headers}
*/
static fromPayload(payload) {
let headers = new Headers();
if (!payload)
return headers;
for (let key in payload)
headers.set(key, payload[key]);
return headers;
}
constructor() {
/** @type {!Map<string, string>} */
this._headers = new Map();
}
/**
* @param {string} name
* @param {string} value
*/
append(name, value) {
name = name.toLowerCase();
this._headers.set(name, value);
}
/**
* @param {string} name
*/
delete(name) {
name = name.toLowerCase();
this._headers.delete(name);
}
/**
* @return {!Iterator}
*/
entries() {
return this._headers.entries();
}
/**
* @param {string} name
* @return {?string}
*/
get(name) {
name = name.toLowerCase();
return this._headers.get(name);
}
/**
* @param {string} name
* @return {boolean}
*/
has(name) {
name = name.toLowerCase();
return this._headers.has(name);
}
/**
* @return {!Iterator}
*/
keys() {
return this._headers.keys();
}
/**
* @return {!Iterator}
*/
values() {
return this._headers.values();
}
/**
* @param {string} name
* @param {string} value
*/
set(name, value) {
name = name.toLowerCase();
this._headers.set(name, value);
}
}
class Request {
/**
* @param {!NetworkManager} networkManager
* @param {string} id
* @param {!Object} payload
*/
constructor(networkManager, id, payload) {
this._networkManager = networkManager;
this._id = id;
this.url = payload.url;
this.method = payload.method;
this.headers = Headers.fromPayload(payload.headers);
this.postData = payload.postData;
}
/**
* @return {?Response}
*/
response() {
return this._networkManager._idToResponse.get(this._id) || null;
}
}
class Response {
/**
* @param {!NetworkManager} networkManager
* @param {string} id
* @param {!Object} payload
*/
constructor(networkManager, id, payload) {
this._networkManager = networkManager;
this._id = id;
this.headers = Headers.fromPayload(payload.headers);
this.ok = payload.status >= 200 && payload.status <= 299;
this.status = payload.status;
this.statusText = payload.statusText;
this.url = payload.url;
}
/**
* @return {?Response}
*/
request() {
return this._networkManager._idToRequest.get(this._id) || null;
}
}
class InterceptedRequest {
/**
* @param {!Connection} client
* @param {string} interceptionId
* @param {!Object} payload
*/
constructor(client, interceptionId, payload) {
this._client = client;
this._interceptionId = interceptionId;
this._handled = false;
this.url = payload.url;
this.method = payload.method;
this.headers = Headers.fromPayload(payload.headers);
this.postData = payload.postData;
}
abort() {
console.assert(!this._handled, 'This request is already handled!');
this._handled = true;
this._client.send('Network.continueInterceptedRequest', {
interceptionId: this._interceptionId,
errorReason: 'Aborted'
});
}
continue() {
console.assert(!this._handled, 'This request is already handled!');
this._handled = true;
let headers = {};
for (let entry of this.headers.entries())
headers[entry[0]] = entry[1];
this._client.send('Network.continueInterceptedRequest', {
interceptionId: this._interceptionId,
url: this.url,
method: this.method,
postData: this.postData,
headers: headers
});
}
/**
* @return {boolean}
*/
isHandled() {
return this._handled;
}
}
NetworkManager.Events = {
Request: 'request',
Response: 'response',
RequestFailed: 'requestfailed',
RequestFinished: 'requestfinished',
};
module.exports = NetworkManager;

View File

@ -17,7 +17,7 @@
let fs = require('fs');
let EventEmitter = require('events');
let mime = require('mime');
let {Request, InterceptedRequest} = require('./Request');
let NetworkManager = require('./NetworkManager');
let Navigator = require('./Navigator');
let Dialog = require('./Dialog');
let FrameManager = require('./FrameManager');
@ -35,10 +35,13 @@ class Page extends EventEmitter {
client.send('Runtime.enable', {}),
client.send('Security.enable', {}),
]);
let expression = helper.evaluationString(() => window.devicePixelRatio);
let {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true });
let screenDPIExpression = helper.evaluationString(() => window.devicePixelRatio);
let {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression: screenDPIExpression, returnByValue: true });
let userAgentExpression = helper.evaluationString(() => window.navigator.userAgent);
let {result:{value: userAgent}} = await client.send('Runtime.evaluate', { expression: userAgentExpression, returnByValue: true });
let frameManager = await FrameManager.create(client);
let page = new Page(client, frameManager, screenDPI);
let networkManager = new NetworkManager(client, userAgent);
let page = new Page(client, frameManager, networkManager, screenDPI);
// Initialize default page size.
await page.setViewportSize({width: 400, height: 300});
return page;
@ -47,12 +50,14 @@ class Page extends EventEmitter {
/**
* @param {!Connection} client
* @param {!FrameManager} frameManager
* @param {!NetworkManager} networkManager
* @param {number} screenDPI
*/
constructor(client, frameManager, screenDPI) {
constructor(client, frameManager, networkManager, screenDPI) {
super();
this._client = client;
this._frameManager = frameManager;
this._networkManager = networkManager;
this._screenDPI = screenDPI;
this._extraHeaders = {};
/** @type {!Map<string, function>} */
@ -68,12 +73,13 @@ class Page extends EventEmitter {
this._frameManager.on(FrameManager.Events.FrameDetached, event => this.emit(Page.Events.FrameDetached, event));
this._frameManager.on(FrameManager.Events.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event));
client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response));
client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event));
client.on('Network.requestWillBeSent', event => this.emit(Page.Events.Request, new Request(event.request)));
this._networkManager.on(NetworkManager.Events.Request, event => this.emit(Page.Events.Request, event));
this._networkManager.on(NetworkManager.Events.Response, event => this.emit(Page.Events.Response, event));
this._networkManager.on(NetworkManager.Events.RequestFailed, event => this.emit(Page.Events.RequestFailed, event));
this._networkManager.on(NetworkManager.Events.RequestFinished, event => this.emit(Page.Events.RequestFinished, event));
client.on('Page.loadEventFired', event => this.emit(Page.Events.Load));
client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event));
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
@ -98,16 +104,7 @@ class Page extends EventEmitter {
* @param {?function(!InterceptedRequest)} interceptor
*/
async setRequestInterceptor(interceptor) {
this._requestInterceptor = interceptor;
await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor});
}
/**
* @param {!Object} event
*/
_onRequestIntercepted(event) {
let request = new InterceptedRequest(this._client, event.InterceptionId, event.request);
this._requestInterceptor(request);
return this._networkManager.setRequestInterceptor(interceptor);
}
/**
@ -176,35 +173,30 @@ class Page extends EventEmitter {
* @param {!Object} headers
* @return {!Promise}
*/
async setExtraHTTPHeaders(headers) {
this._extraHeaders = {};
// Note: header names are case-insensitive.
for (let key of Object.keys(headers))
this._extraHeaders[key.toLowerCase()] = headers[key];
return this._client.send('Network.setExtraHTTPHeaders', { headers });
async setHTTPHeaders(headers) {
return this._networkManager.setHTTPHeaders(headers);
}
/**
* @return {!Object}
*/
extraHTTPHeaders() {
return Object.assign({}, this._extraHeaders);
httpHeaders() {
return this._networkManager.httpHeaders();
}
/**
* @param {string} userAgent
* @return {!Promise}
*/
async setUserAgentOverride(userAgent) {
this._userAgent = userAgent;
return this._client.send('Network.setUserAgentOverride', { userAgent });
async setUserAgent(userAgent) {
return this._networkManager.setUserAgent(userAgent);
}
/**
* @return {string}
*/
userAgentOverride() {
return this._userAgent;
userAgent() {
return this._networkManager.userAgent();
}
/**
@ -630,8 +622,9 @@ Page.Events = {
Dialog: 'dialog',
Error: 'error',
Request: 'request',
ResourceLoadingFailed: 'resourceloadingfailed',
ResponseReceived: 'responsereceived',
Response: 'response',
RequestFailed: 'requestfailed',
RequestFinished: 'requestfinished',
FrameAttached: 'frameattached',
FrameDetached: 'framedetached',
FrameNavigated: 'framenavigated',

View File

@ -1,159 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Headers {
/**
* @param {?Object} payload
* @return {!Headers}
*/
static fromPayload(payload) {
let headers = new Headers();
if (!payload)
return headers;
for (let key in payload)
headers.set(key, payload[key]);
return headers;
}
constructor() {
/** @type {!Map<string, string>} */
this._headers = new Map();
}
/**
* @param {string} name
* @param {string} value
*/
append(name, value) {
name = name.toLowerCase();
this._headers.set(name, value);
}
/**
* @param {string} name
*/
delete(name) {
name = name.toLowerCase();
this._headers.delete(name);
}
/**
* @return {!Iterator}
*/
entries() {
return this._headers.entries();
}
/**
* @param {string} name
* @return {?string}
*/
get(name) {
name = name.toLowerCase();
return this._headers.get(name);
}
/**
* @param {string} name
* @return {boolean}
*/
has(name) {
name = name.toLowerCase();
return this._headers.has(name);
}
/**
* @return {!Iterator}
*/
keys() {
return this._headers.keys();
}
/**
* @return {!Iterator}
*/
values() {
return this._headers.values();
}
/**
* @param {string} name
* @param {string} value
*/
set(name, value) {
name = name.toLowerCase();
this._headers.set(name, value);
}
}
class Request {
/**
* @param {!Object} payload
*/
constructor(payload) {
this.url = payload.url;
this.method = payload.method;
this.headers = Headers.fromPayload(payload.headers);
this.postData = payload.postData;
}
}
class InterceptedRequest extends Request {
/**
* @param {!Connection} client
* @param {string} interceptionId
* @param {!Object} payload
*/
constructor(client, interceptionId, payload) {
super(payload);
this._client = client;
this._interceptionId = interceptionId;
this._handled = false;
}
abort() {
console.assert(!this._handled, 'This request is already handled!');
this._handled = true;
this._client.send('Network.continueInterceptedRequest', {
interceptionId: this._interceptionId,
errorReason: 'Aborted'
});
}
continue() {
console.assert(!this._handled, 'This request is already handled!');
this._handled = true;
let headers = {};
for (let entry of this.headers.entries())
headers[entry[0]] = entry[1];
this._client.send('Network.continueInterceptedRequest', {
interceptionId: this._interceptionId,
url: this.url,
method: this.method,
postData: this.postData,
headers: headers
});
}
/**
* @return {boolean}
*/
isHandled() {
return this._handled;
}
}
module.exports = {Request, InterceptedRequest};

View File

@ -55,8 +55,8 @@ class WebPage {
this._onError = noop;
this._pageEvents = new AsyncEmitter(this._page);
this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response));
this._pageEvents.on(PageEvents.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event));
this._pageEvents.on(PageEvents.Response, response => this._onResponseReceived(response));
this._pageEvents.on(PageEvents.RequestFailed, event => (this.onResourceError || noop).call(null, event));
this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg));
this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message));
this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message));
@ -95,15 +95,8 @@ class WebPage {
_onResponseReceived(response) {
if (!this.onResourceReceived)
return;
let headers = [];
for (let key in response.headers) {
headers.push({
name: key,
value: response.headers[key]
});
}
response.headers = headers;
this.onResourceReceived.call(null, response);
let phantomResponse = new PhantomResponse(response);
this.onResourceReceived.call(null, phantomResponse);
}
/**
@ -125,14 +118,14 @@ class WebPage {
* @return {!Object}
*/
get customHeaders() {
return this._page.extraHTTPHeaders();
return this._page.httpHeaders();
}
/**
* @param {!Object} value
*/
set customHeaders(value) {
await(this._page.setExtraHTTPHeaders(value));
await(this._page.setHTTPHeaders(value));
}
/**
@ -327,14 +320,14 @@ class WebPageSettings {
* @param {string} value
*/
set userAgent(value) {
await(this._page.setUserAgentOverride(value));
await(this._page.setUserAgent(value));
}
/**
* @return {string}
*/
get userAgent() {
return this._page.userAgentOverride();
return this._page.userAgent();
}
}
@ -366,6 +359,24 @@ class PhantomRequest {
}
}
class PhantomResponse {
/**
* @param {!Response} response
*/
constructor(response) {
this.url = response.url;
this.status = response.status;
this.statusText = response.statusText;
this.headers = [];
for (let entry of response.headers.entries()) {
this.headers.push({
name: entry[0],
value: entry[1]
});
}
}
}
class RequestData {
/**
* @param {!InterceptedRequest} request
@ -373,7 +384,7 @@ class RequestData {
constructor(request) {
this.url = request.url,
this.headers = {};
for (let entry in request.headers.entries())
for (let entry of request.headers.entries())
this.headers[entry[0]] = entry[1];
}
}

View File

@ -254,8 +254,8 @@ describe('Puppeteer', function() {
let success = await page.navigate(EMPTY_PAGE);
expect(success).toBe(true);
}));
it('should show extraHTTPHeaders', SX(async function() {
await page.setExtraHTTPHeaders({
it('should show custom HTTP headers', SX(async function() {
await page.setHTTPHeaders({
foo: 'bar'
});
page.setRequestInterceptor(request => {
@ -272,11 +272,11 @@ describe('Puppeteer', function() {
else
request.continue();
});
let failedResources = 0;
page.on('resourceloadingfailed', event => ++failedResources);
let failedRequests = 0;
page.on('requestfailed', event => ++failedRequests);
let success = await page.navigate(STATIC_PREFIX + '/one-style.html');
expect(success).toBe(true);
expect(failedResources).toBe(1);
expect(failedRequests).toBe(1);
}));
it('should amend HTTP headers', SX(async function() {
await page.navigate(EMPTY_PAGE);
@ -478,6 +478,76 @@ describe('Puppeteer', function() {
expect(await page.evaluate(() => result)).toBe('Clicked');
}));
});
describe('Page.setUserAgent', function() {
it('should work', SX(async function() {
expect(page.userAgent()).toContain('Mozilla');
page.setUserAgent('foobar');
page.navigate(EMPTY_PAGE);
let request = await staticServer.waitForRequest('/empty.html');
expect(request.headers['user-agent']).toBe('foobar');
}));
});
describe('Page.setHTTPHeaders', function() {
it('should work', SX(async function() {
expect(page.httpHeaders()).toEqual({});
page.setHTTPHeaders({'foo': 'bar'});
expect(page.httpHeaders()).toEqual({'foo': 'bar'});
page.navigate(EMPTY_PAGE);
let request = await staticServer.waitForRequest('/empty.html');
expect(request.headers['foo']).toBe('bar');
}));
});
describe('Network Events', function() {
it('Page.Events.Request', SX(async function() {
let requests = [];
page.on('request', request => requests.push(request));
await page.navigate(EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url).toBe(EMPTY_PAGE);
expect(requests[0].method).toBe('GET');
expect(requests[0].response()).toBeTruthy();
}));
it('Page.Events.Response', SX(async function() {
let responses = [];
page.on('response', response => responses.push(response));
await page.navigate(EMPTY_PAGE);
expect(responses.length).toBe(1);
expect(responses[0].url).toBe(EMPTY_PAGE);
expect(responses[0].status).toBe(200);
expect(responses[0].ok).toBe(true);
expect(responses[0].request()).toBeTruthy();
}));
it('Page.Events.RequestFailed', SX(async function() {
page.setRequestInterceptor(request => {
if (request.url.endsWith('css'))
request.abort();
else
request.continue();
});
let failedRequests = [];
page.on('requestfailed', request => failedRequests.push(request));
await page.navigate(STATIC_PREFIX + '/one-style.html');
expect(failedRequests.length).toBe(1);
expect(failedRequests[0].url).toContain('one-style.css');
expect(failedRequests[0].response()).toBe(null);
}));
it('Page.Events.RequestFinished', SX(async function() {
let requests = [];
page.on('requestfinished', request => requests.push(request));
await page.navigate(EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url).toBe(EMPTY_PAGE);
expect(requests[0].response()).toBeTruthy();
}));
it('should fire events in proper order', SX(async function() {
let events = [];
page.on('request', request => events.push('request'));
page.on('response', response => events.push('response'));
page.on('requestfinished', request => events.push('requestfinished'));
await page.navigate(EMPTY_PAGE);
expect(events).toEqual(['request', 'response', 'requestfinished']);
}));
});
});
// Since Jasmine doesn't like async functions, they should be wrapped