-
Notifications
You must be signed in to change notification settings - Fork 2
/
puppeteer_cls.js
123 lines (110 loc) · 3.62 KB
/
puppeteer_cls.js
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
// Script to get CLS information from a page, with a lot of help from
// https://addyosmani.com/blog/puppeteer-recipes/
const { trace } = require('console');
const util = require('util');
const fs = require('fs');
const puppeteer = require('puppeteer-core');
const {default: PQueue} = require('p-queue');
function traceFilename(url) {
return url.replace(/[\W]/g, '_') + '.json';
}
function txtFilename(url) {
return url.replace(/[\W]/g, '_') + '.txt';
}
function getTracePath(url, outDir) {
if (!outDir.endsWith('/')) {
outDir = outDir + '/';
}
return outDir + traceFilename(url);
}
function getTxtPath(url, outDir) {
if (!outDir.endsWith('/')) {
outDir = outDir + '/';
}
return outDir + txtFilename(url);
}
// This method is injected into the document. Based on documentation in
// https://web.dev/cls
function calcCLS() {
window.cumulativeLayoutShiftScore = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
window.cumulativeLayoutShiftScore += entry.value;
}
}
});
observer.observe({ type: 'layout-shift', buffered: true });
}
// Use puppeteer to get CLS.
async function getCLS(url, traceOut, chromePath) {
const browser = await puppeteer.launch({
executablePath: chromePath,
args: ['--no-sandbox'],
timeout: 10000
});
try {
const page = await browser.newPage();
// Use devtools emulation of phone with 4G network.
// Need to do network emulation in order to slow down enough to generate layout
// shifts. Unclear why this is true as CLS issue reproduce manually with no
// network emulation.
const Regular4G = {
'offline': false,
'downloadThroughput': 4 * 1024 * 1024 / 8,
'uploadThroughput': 3 * 1024 * 1024 / 8,
'latency': 20
};
const client = await page.target().createCDPSession();
await client.send('Network.emulateNetworkConditions', Regular4G);
// Use a consistent device; Moto G4 which I did most manual testing with in
// devtools emulation is unavailable but it matches Nexus 5.
await page.emulate(puppeteer.devices['Nexus 5']);
await page.evaluateOnNewDocument(calcCLS);
// Wait for 3 seconds after load.
if (traceOut) {
await page.tracing.start({ screenshots: true, path: traceOut, categories: ['loading', 'disabled-by-default-layout_shift.debug'] });
}
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
await page.waitForTimeout(3000);
let cls = await page.evaluate(() => {
return window.cumulativeLayoutShiftScore;
});
if (traceOut) {
await page.tracing.stop();
}
browser.close();
return cls;
} catch (error) {
console.log(error);
browser.close();
}
}
async function getAllCLS(url, outDir, numRuns, chromePath) {
let allScores = [];
for (let i = 0; i < numRuns; i++) {
console.log('Starting run ', i, ' for ', url);
let cls = await getCLS(url, undefined, chromePath);
allScores.push(cls);
}
console.log(url, allScores);
fs.writeFileSync(getTxtPath(url, outDir), allScores.join('\n'));
clsForTrace = await getCLS(url, getTracePath(url, outDir), chromePath);
return allScores;
}
(async () => {
const args = process.argv.slice(2);
const data = fs.readFileSync(args[3], 'utf8');
const urls = data.split(/\s/);
const numRuns = args[0];
const outDir = args[1];
const chromePath = args[2];
util.promisify(fs.mkdir)(outDir, { recursive: true });
const queue = new PQueue({
concurrency: 20
});
for (let url of urls) {
if (!url) continue;
queue.add(async () => getAllCLS(url, outDir, numRuns, chromePath));
}
})();