Created
August 21, 2020 09:56
-
-
Save martinschierle/1d412cf1b8bc76e726e8c9ceddca1071 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const puppeteer = require('puppeteer'); | |
const { createCanvas, loadImage } = require('canvas') | |
var fs = require('fs'); | |
const fsExtra = require('fs-extra') | |
const mergeImg = require('merge-img'); | |
const mustache = require('mustache') | |
let MAX_URLS = 50; | |
let TEMPLATE = fs.readFileSync('template.html', 'utf8'); | |
const Good3G = { | |
'offline': false, | |
'downloadThroughput': 1.5 * 1024 * 1024 / 8, | |
'uploadThroughput': 750 * 1024 / 8, | |
'latency': 40 | |
}; | |
const phone = puppeteer.devices['Nexus 5X']; | |
function injectJs() { | |
window.cls = 0; | |
let po = new PerformanceObserver((list) => { | |
for (const entry of list.getEntries()) { | |
window.cls += entry.value; | |
} | |
}); | |
po.observe({type: 'layout-shift', buffered: true}); | |
} | |
async function getBrowser(allowWebFonts) { | |
let args = ['--no-sandbox']; | |
if(!allowWebFonts) { | |
args.push("--disable-remote-fonts"); | |
} | |
const browser = await puppeteer.launch({ | |
args: args, | |
//headless: false, | |
//executablePath: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary', | |
timeout: 10000 | |
}); | |
return browser; | |
} | |
async function getNewPage(browser) { | |
const page = await browser.newPage(); | |
await page.emulate(phone); | |
await page.setCacheEnabled(false); // no cache, so that we can reuse the same page several times | |
return page; | |
} | |
async function getCLS(page, url) { | |
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000}); | |
await page.waitFor(1000); // let's give it a bit more time, to be sure everything's loaded | |
console.log("Injecting JS..."); | |
await Promise.race([ | |
page.evaluate(injectJs), | |
page.waitFor(5000) | |
]); | |
page.waitFor(2000); | |
console.log("Gathering data..."); | |
let cls = await Promise.race([ | |
page.evaluate(function() {return window.cls}), | |
page.waitFor(5000) | |
]); | |
return cls; | |
} | |
async function doBatch(urls, max) { | |
// reset output file and images dir | |
fsExtra.emptyDirSync("output"); | |
fs.mkdirSync("output/images"); | |
let results = []; | |
for(var k = 0; k < Math.min(max, urls.length); k++) { | |
const url = urls[k]; | |
console.log("Processing: " + url); | |
try { | |
let withFontBrowser = await getBrowser(true); | |
let withFontPage = await getNewPage(withFontBrowser); | |
let withFontCLS = (await getCLS(withFontPage, url) + await getCLS(withFontPage, url))/2; // two runs to average things out | |
await withFontPage.screenshot({path: 'output/images/withfonts.png'}); | |
await withFontBrowser.close(); | |
let noFontsBrowser = await getBrowser(false); | |
let noFontsPage = await getNewPage(noFontsBrowser); | |
let noFontCLS = (await getCLS(noFontsPage, url) + await getCLS(noFontsPage, url))/2; // two runs to average things out | |
await noFontsPage.screenshot({path: 'output/images/nofonts.png'}); | |
await noFontsBrowser.close(); | |
let screenshotname = "images/" + new URL(url).hostname+'.png'; | |
mergeImg(['output/images/withfonts.png', 'output/images/nofonts.png']) | |
.then((img) => { | |
// Save image as file | |
img.write("output/"+screenshotname, () => console.log('done')); | |
}); | |
results.push({url: url, withFontCLS: withFontCLS, noFontCLS: noFontCLS, diff:(noFontCLS - withFontCLS), screenshot: screenshotname}); | |
} catch (error) { | |
console.log(error); | |
//process.exit(0); | |
} | |
} | |
// write out result html | |
results.sort((a, b) => (a.diff < b.diff) ? -1 : 1) | |
var rendered = mustache.render(TEMPLATE, {items: results}); | |
fs.writeFileSync('output/index.html', rendered) | |
} | |
let urls = fs.readFileSync('input.csv').toString().split("\n"); | |
doBatch(urls, 200).then(res => {console.log("Done!");process.exit(0);}); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<style> | |
#mainTable tr:nth-child(odd){ | |
background-color: lightblue; | |
} | |
thead { | |
font-weight: bold; | |
background-color: lightgrey; | |
} | |
.screenshot { | |
margin:20px; | |
width:150px; | |
} | |
.heatmap { | |
width:400px; | |
} | |
td { | |
word-break:break-all; | |
width: 16%; | |
} | |
</style> | |
</head> | |
<body onload="renderHello()"> | |
<h1>Analysis results for Font-related layout shifts</h1> | |
<table> | |
<thead> | |
<tr> | |
<td width="10%">URL</td> | |
<td>CLS With Web-Fonts</td> | |
<td>CLS Without Webfonts</td> | |
<td>CLS Diff</td> | |
<td>Screenshot</td> | |
</tr> | |
</thead> | |
<tbody id="mainTable"> | |
{{#items}} | |
<tr> | |
<td>{{url}}</td> | |
<td>{{withFontCLS}}</td> | |
<td>{{noFontCLS}}</td> | |
<td>{{diff}}</td> | |
<td><img src="{{screenshot}}" width="400px"></td> | |
</tr> | |
{{/items}} | |
</tbody> | |
</table> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment