-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathperformance_test.js
More file actions
143 lines (125 loc) · 4.73 KB
/
performance_test.js
File metadata and controls
143 lines (125 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import * as ChromeLauncher from 'chrome-launcher';
import CRI from 'chrome-remote-interface';
import csv from 'csvtojson';
import Debug from 'debug';
const debug = Debug('fcp-performance');
import fs from 'node:fs';
import * as si from 'systeminformation';
import { program } from 'commander';
import { resolve } from 'node:path';
class TestRun {
constructor(productVersion, measurements, datetime, osInfo, browserArgs) {
this.productVersion = productVersion;
this.measurements = measurements;
this.datetime = datetime;
this.osInfo = osInfo;
this.browserArgs = browserArgs;
}
}
class Measurement {
constructor(url, fcp, datetime) {
this.url = url;
this.fcp = fcp;
this.datetime = datetime;
}
}
const getUrls = async (filepath) => {
const domains = await csv().fromFile(filepath);
return domains.map(row => 'https://' + row.domain);
}
const measureFCPPerformance = async (client, url) => {
try {
const { Page, Runtime } = client;
debug("navigating");
const navigationPromise = Page.navigate({ url });
await Promise.race([navigationPromise,
new Promise((_, reject) => setTimeout(() => reject('timeout'), 10000))]);
if (navigationPromise.errorText) {
throw new Error(navigationPromise.errorText);
}
debug("waiting for fcp");
const startTime = await Runtime.evaluate({
expression: `new Promise((resolve, reject) => {
new PerformanceObserver((entryList) => { \
for (const entry of entryList.getEntriesByName('first-contentful-paint')) { \
resolve(entry.startTime); \
} \
}).observe({type: 'paint', buffered: true});
setTimeout(() => reject('timeout'), 10000);
})`,
awaitPromise: true
});
debug("fcp", startTime);
await Page.stopLoading();
return startTime.result.value;
} catch (error) {
debug(error);
return Infinity;
}
}
const runTest = async (options) => {
const browserArgsList = options.browserArgs.split(' ');
debug("browserArgsList", browserArgsList);
let chrome = null;
if (options.launchChrome) {
debug("Launching chrome");
chrome = await ChromeLauncher.launch({
port: options.port,
chromeFlags: browserArgsList
});
}
try {
debug("getting protocol interface");
const client = await CRI({ host: options.host, port: options.port });
const { Page, Browser } = client;
debug("getting browser version");
const version = await Browser.getVersion();
debug("connected to browser", version);
debug("enabling page events");
await Page.enable();
const testsStartTime = new Date().toISOString();
const urls = await getUrls(options.input);
let measurements = [];
for (let i = 0; i < options.repeat; i++) {
for (const url of urls) {
const startTime = new Date().toISOString();
const fcpTime = await measureFCPPerformance(client, url);
console.log(`FCP for ${url} is ${fcpTime}ms`);
measurements.push(new Measurement(url, fcpTime, startTime));
}
}
// Save system information for the test run.
const osInfo = await si.osInfo();
delete osInfo.serial;
delete osInfo.hostname;
delete osInfo.fqdn;
// Save results to a JSON file.
const testRun = new TestRun(version.product, measurements, testsStartTime, osInfo, browserArgsList);
fs.writeFileSync(options.output, JSON.stringify(testRun));
await client.close();
} catch (error) {
console.error(error);
} finally {
if (chrome) {
chrome.kill();
}
}
}
program
.name('fcp-performance')
.description('A tool to measure First Contentful Paint performance of a list of URLs.')
.version('0.1.0');
// Define options and commands here
program
.option('-i, --input <filename>', 'Input filename containing a list of URLs to test, it should be a CSV file with a column named "domain" containing the domain names. Default is "moz.com_top500.csv"', 'moz.com_top500.csv')
.option('-o, --output <filename>', 'Name for an output file to save the JSON results. Default is "fcp-performance.json"', 'fcp-performance.json')
.option('-b, --browser-args <args>', 'Arguments to pass to the browser instance. Default is "--disable-gpu --no-sandbox --headless"', '--disable-gpu --no-sandbox --headless')
.option('-l, --launch-chrome', 'Launch Chrome in a separate process. Default is true', true)
.option('-h, --host <hostname>', 'Hostname to use to connect to Chrome DevTools. Default is localhost', 'localhost')
.option('-p, --port <port>', 'Port to use to connect to Chrome DevTools. Default is 9222', 9222)
.option('-r, --repeat <n>', 'Number of times to repeat the test. Default is 1', 1);
// Parse the command line arguments
program.parse(process.argv);
const options = program.opts();
debug("options", options);
await runTest(options);