Skip to content

Commit

Permalink
feat: use network events from pw (#488)
Browse files Browse the repository at this point in the history
* feat: use network events from pw

* fix tls logic

* address feedback
  • Loading branch information
vigneshshanmugam authored Apr 12, 2022
1 parent b2c6c76 commit 2ae1448
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 513 deletions.
29 changes: 0 additions & 29 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -219,32 +219,3 @@ This product relies on `expect`
*
*/
---------------------------------------------------------------------------
This product relies on `ChromeDevTools/devtools-frontend`

// Copyright 2014 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 changes: 19 additions & 2 deletions __tests__/core/gatherer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,33 @@ describe('Gatherer', () => {
page.click('a'),
]);
await page1.waitForLoadState();

// @ts-ignore
// Experimental browser API https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlink
const downlink = await page1.evaluate(
() => (navigator['connection'] as any).downlink
);

expect(3.5 > downlink && downlink > 2.5).toBe(true);
await Gatherer.dispose(driver);
await Gatherer.stop();
});

it('dont throw for closed popups before load', async () => {
const driver = await Gatherer.setupDriver({
wsEndpoint,
networkConditions,
});
const { page, context } = driver;
await page.goto(server.TEST_PAGE);
await page.setContent(
'<a target=_blank rel=noopener href="/popup.html">popup</a>'
);
const [page1] = await Promise.all([
context.waitForEvent('page'),
page.click('a'),
]);
await page1.close();
await Gatherer.dispose(driver);
await Gatherer.stop();
});
});
});
26 changes: 0 additions & 26 deletions __tests__/fixtures/networkinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export const NETWORK_INFO: Array<NetworkInfo> = [
response: {
url: 'http://localhost:56899/index',
status: 200,
protocol: 'http/1.1',
statusText: 'OK',
headers: {
'content-type': 'text/html',
Expand All @@ -62,26 +61,6 @@ export const NETWORK_INFO: Array<NetworkInfo> = [
remotePort: 56899,
fromServiceWorker: false,
redirectURL: undefined,
timing: {
requestTime: 300869.367875,
proxyStart: -1,
proxyEnd: -1,
dnsStart: 0.25,
dnsEnd: 0.25,
connectStart: 0.25,
connectEnd: 0.5,
sslStart: -1,
sslEnd: -1,
workerStart: -1,
workerReady: -1,
workerFetchStart: -1,
workerRespondWithSettled: -1,
sendStart: 0.5,
sendEnd: 0.5,
pushStart: 0,
pushEnd: 0,
receiveHeadersEnd: 5.125,
},
body: { bytes: 194 },
},
isNavigationRequest: true,
Expand All @@ -92,8 +71,6 @@ export const NETWORK_INFO: Array<NetworkInfo> = [
transferSize: 194,
timings: {
blocked: 0.24999998277053237,
queueing: 0.6249999860301614,
proxy: -1,
dns: 0,
ssl: -1,
connect: 0.2500000409781933,
Expand Down Expand Up @@ -126,7 +103,6 @@ export const NETWORK_INFO: Array<NetworkInfo> = [
status: -1,
mimeType: 'x-unknown',
headers: {},
timing: null,
},
isNavigationRequest: false,
requestSentTime: 300869.377,
Expand All @@ -136,8 +112,6 @@ export const NETWORK_INFO: Array<NetworkInfo> = [
transferSize: 0,
timings: {
blocked: 79.75000003352761,
queueing: -1,
proxy: -1,
dns: -1,
ssl: -1,
connect: -1,
Expand Down
167 changes: 70 additions & 97 deletions __tests__/plugins/network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/

import { Gatherer } from '../../src/core/gatherer';
import { NetworkManager, calculateTimings } from '../../src/plugins/network';
import { NetworkManager } from '../../src/plugins/network';
import { Server } from '../utils/server';
import { wsEndpoint } from '../utils/test-config';

Expand Down Expand Up @@ -52,12 +52,29 @@ describe('network', () => {
step: null,
timestamp: expect.any(Number),
url: server.TEST_PAGE,
request: expect.any(Object),
response: expect.any(Object),
type: 'Document',
request: {
url: server.TEST_PAGE,
method: 'GET',
body: {
bytes: 0,
},
bytes: expect.any(Number),
},
response: {
url: server.TEST_PAGE,
status: 200,
statusText: 'OK',
body: {
bytes: expect.any(Number),
},
bytes: expect.any(Number),
},
type: 'document',
requestSentTime: expect.any(Number),
loadEndTime: expect.any(Number),
responseReceivedTime: expect.any(Number),
resourceSize: expect.any(Number),
transferSize: expect.any(Number),
timings: expect.any(Object),
});
});
Expand Down Expand Up @@ -106,8 +123,8 @@ describe('network', () => {
});
const netinfo = await network.stop();
expect(netinfo[0]).toMatchObject({
resourceSize: 10,
transferSize: expect.any(Number),
resourceSize: 0,
transferSize: 10,
});
await Gatherer.stop();
});
Expand All @@ -121,57 +138,60 @@ describe('network', () => {
await network.start();

const delayTime = 20;
server.route('/delay20', async (req, res) => {
server.route('/abort', async (req, res) => {
await delay(delayTime);
res.destroy();
});
server.route('/index', async (_, res) => {
res.setHeader('content-type', 'text/html');
res.end(`<script src=${server.PREFIX}/delay20 />`);
res.end(`<script src=${server.PREFIX}/abort />`);
});

await driver.page.goto(server.PREFIX + '/index');
await driver.page.goto(server.PREFIX + '/index', {
waitUntil: 'networkidle',
});
await driver.page.waitForLoadState();
await Gatherer.stop();
const netinfo = await network.stop();
expect(netinfo.length).toBe(2);
expect(netinfo[1]).toMatchObject({
url: `${server.PREFIX}/delay20`,
url: `${server.PREFIX}/abort`,
response: {
headers: {},
mimeType: 'x-unknown',
status: -1,
timing: null,
redirectURL: '',
},
timings: expect.any(Object),
});
expect(netinfo[1].timings.total).toBeGreaterThan(delayTime);
expect(netinfo[1].timings.total).toEqual(netinfo[1].timings.blocked);
});

it('timings for chunked response', async () => {
const driver = await Gatherer.setupDriver({ wsEndpoint });
await driver.client.send('Network.setCacheDisabled', {
cacheDisabled: true,
});
const network = new NetworkManager(driver);
await network.start();

const delayTime = 100;
server.route('/chunked', async (req, res) => {
await delay(delayTime);
res.writeHead(200, {
'content-type': 'application/javascript',
});
res.write('a');
await delay(delayTime);
res.write('b');
await delay(delayTime);
return res.end('c');
return res.end('b');
});
server.route('/index', async (_, res) => {
res.setHeader('content-type', 'text/html');
res.end(`<script src=${server.PREFIX}/chunked />`);
});

await driver.page.goto(server.PREFIX + '/index');
await driver.page.waitForLoadState();
await driver.page.goto(server.PREFIX + '/index', {
waitUntil: 'networkidle',
});
await Gatherer.stop();
const netinfo = await network.stop();
expect(netinfo.length).toBe(2);
Expand All @@ -180,89 +200,42 @@ describe('network', () => {
response: expect.any(Object),
timings: expect.any(Object),
});
expect(netinfo[1].timings.total).toBeGreaterThan(delayTime);
const timings = netinfo[1].timings;
expect(timings.wait).toBeGreaterThan(delayTime);
expect(timings.receive).toBeGreaterThan(delayTime);
expect(timings.total).toBeGreaterThan(timings.wait + timings.receive);
});

describe('waterfall timing calculation', () => {
const getEvent = () => {
return {
response: {
// requestTime is in seconds, rest of the timing.* is in milliseconds
timing: {
requestTime: 1,
proxyStart: -1,
proxyEnd: -1,
dnsStart: 0.1,
dnsEnd: 26,
connectStart: 26,
connectEnd: 92.669,
sslStart: 40,
sslEnd: 92,
sendStart: 94,
sendEnd: 95,
receiveHeadersEnd: 2350,
},
},
requestSentTime: 1,
loadEndTime: 3,
responseReceivedTime: 2,
};
};

it('calculate timings for a request event', () => {
const record = getEvent();
const timings = calculateTimings(record as any);
expect(timings).toEqual({
blocked: 0.09999999999998899,
queueing: -1,
proxy: -1,
dns: 25.900000000000034,
ssl: 52.00000000000004,
connect: 66.66899999999987,
send: 0.9999999999998899,
wait: 905,
receive: 1000,
total: 2000,
});
it('capture network data from popups', async () => {
const driver = await Gatherer.setupDriver({
wsEndpoint,
});
const { page, context } = driver;
const network = new NetworkManager(driver);
await network.start();

it('when some resource timing data is unavailable', () => {
const record = getEvent();
Object.assign(record.response.timing, {
connectEnd: -1,
dnsStart: -1,
});
const timings = calculateTimings(record as any);
expect(timings).toEqual({
blocked: 26.00000000000002,
connect: -1,
dns: -1,
proxy: -1,
queueing: -1,
receive: 1000,
send: 0.9999999999998899,
ssl: 52.00000000000004,
total: 2000,
wait: 905,
});
});
// Simulate user flow from test page -> popup page
await page.goto(server.TEST_PAGE);
await page.setContent(
'<a target=_blank rel=noopener href="/popup.html">popup</a>'
);
const [page1] = await Promise.all([
context.waitForEvent('page'),
page.click('a'),
]);
await page1.waitForLoadState('load');
expect(await page1.textContent('body')).toEqual('Not found');

it('when complete resource timing is not available', () => {
const record = getEvent();
record.response.timing = null;
const timings = calculateTimings(record as any);
expect(timings).toEqual({
blocked: 1000,
connect: -1,
dns: -1,
proxy: -1,
queueing: -1,
receive: 1000,
send: -1,
ssl: -1,
total: 2000,
wait: -1,
});
});
await Gatherer.stop();
const netinfo = await network.stop();
expect(netinfo.length).toBe(2);
expect(netinfo).toMatchObject([
{
url: server.TEST_PAGE,
},
{
url: `${server.PREFIX}/popup.html`,
},
]);
});
});
Loading

0 comments on commit 2ae1448

Please sign in to comment.