Last active
June 30, 2020 22:21
-
-
Save martinschierle/11873c242cf1973d0f7c6e1fae2eec48 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 devices = require('puppeteer/DeviceDescriptors'); | |
var fs = require('fs'); | |
const mergeImg = require('merge-img'); | |
const ampToolboxCacheUrl = require('amp-toolbox-cache-url').createCacheUrl; | |
const Good3G = { | |
'offline': false, | |
'downloadThroughput': 1.5 * 1024 * 1024 / 8, | |
'uploadThroughput': 750 * 1024 / 8, | |
'latency': 40 | |
}; | |
const phone = devices['Nexus 5X']; | |
function injectJs() { | |
window.lcp = {value: -1, size: -1}; | |
window.cls = {value: 0}; | |
window.lcp_elem = null; | |
window.cls_elems = []; | |
var cssPath = function (el) { | |
var path = []; | |
while ( | |
(el.nodeName.toLowerCase() != 'html') && | |
(el = el.parentNode) && | |
path.unshift(el.nodeName.toLowerCase() + | |
(el.id ? '#' + el.id : '') + | |
(el.className ? '.' + el.className.replace(/\s+/g, ".") : '')) | |
); | |
return path.join(" > "); | |
} | |
// Create a PerformanceObserver that calls `updateLCP` for each entry. | |
const po = new PerformanceObserver((entryList) => { | |
entryList.getEntries().forEach(function(entry) { | |
console.log(entry); | |
if(entry.size > window.lcp.size) { | |
let e = entry.element; | |
if(!(e instanceof HTMLElement)) e = e.parentElement; | |
window.lcp.size = entry.size; | |
window.lcp.value = entry.startTime; | |
window.lcp_elem = e; | |
window.lcp.tagName = e.tagName; | |
window.lcp.classes = e.getAttribute("class"); | |
window.lcp.path = cssPath(e); | |
} | |
}); | |
}); | |
// Observe entries of type `largest-contentful-paint`, including buffered entries, | |
// i.e. entries that occurred before calling `observe()` below. | |
po.observe({ | |
type: 'largest-contentful-paint', | |
buffered: true, | |
}); | |
try { | |
const cls_po = new PerformanceObserver((list) => { | |
for (const entry of list.getEntries()) { | |
console.log(entry); | |
window.cls.value += entry.value; | |
if(entry.sources && entry.sources.length>0) { | |
// find the source of maximum size | |
for(var i = 0;i < entry.sources.length; i++) { | |
let source = entry.sources[i]; | |
let e = source.node; | |
if(!e) continue; | |
if(!(e instanceof HTMLElement)) e = e.parentElement; | |
window.cls_elems.push(e); | |
} | |
} | |
} | |
}); | |
cls_po.observe({type: 'layout-shift', buffered: true}); | |
} catch (e) { | |
console.log(e.message); | |
// Do nothing if the browser doesn't support this API. | |
} | |
} | |
function highlight() { | |
function highlightElem(elem, color, width) { | |
if(!elem) return; | |
elem.style.border = width + "px solid " + color; | |
elem.style["box-sizing"] = "border-box"; | |
} | |
for(var i = 0; i < window.cls_elems.length; i++) { | |
let elem = window.cls_elems[i]; | |
if(!elem) continue; | |
highlightElem(elem, "green", 3); | |
} | |
highlightElem(window.lcp_elem, "red", 5); | |
} | |
async function doBatch(filename) { | |
/*const browser = await puppeteer.launch({ | |
args: ['--no-sandbox'], | |
timeout: 10000 | |
});*/ | |
const browser = await puppeteer.launch({ | |
headless: true, | |
executablePath: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary', | |
args: ['--no-sandbox', '--disable-notifications'], | |
timeout: 10000 | |
}); | |
const page = await browser.newPage(); | |
//await page.evaluateOnNewDocument(injectJs); | |
//page.on('console', msg => {if(msg.text().indexOf("AMP validation successful") >= 0) validAMP = true;}); | |
await page.emulate(phone); | |
const client = await page.target().createCDPSession(); | |
await client.send('Network.enable'); | |
await client.send('ServiceWorker.enable'); | |
//await client.send('Network.emulateNetworkConditions', Good3G); | |
//await client.send('Emulation.setCPUThrottlingRate', { rate: 4 }); | |
let urls = fs.readFileSync('input.csv').toString().split("\n"); | |
for(var i = 0; i < Math.min(urls.length, 1000); i++) { | |
const url = urls[i]; | |
console.log("Processing: " + url); | |
try { | |
// inject a function with the code from https://web.dev/cls/#measure-cls-in-javascript | |
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000}); | |
//page.on('console', consoleObj => console.log(consoleObj.text())); | |
await page.waitFor(2000); // 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) | |
]); | |
console.log("Highlighting element..."); | |
await Promise.race([ | |
page.evaluate(highlight), | |
page.waitFor(5000) | |
]); | |
console.log("Gathering results..."); | |
let results = await Promise.race([ | |
page.evaluate(function() {return {'cls': window.cls, 'lcp': window.lcp, 'lcp_elem': window.lcp_elem}}), | |
page.waitFor(5000) | |
]); | |
if(!results) { | |
console.log("Couldn't retrieve results."); | |
continue; | |
} | |
let cls = results.cls; | |
let lcp = results.lcp; | |
let lcp_elem = results.lcp_elem; | |
console.log("Getting screenshot..."); | |
let screenshot_path = "images/" + new URL(url).hostname + ".jpeg"; | |
try { | |
await page.screenshot({path: screenshot_path, type: "jpeg", "quality": 30}); | |
} catch(e) {console.log("Can't take screenshot: " + e.message)} | |
out = new URL(url).hostname + "," + url + "," + lcp.value + ", " + cls.value + "," + lcp.tagName + "," + lcp.classes + "," + lcp.path + "," + screenshot_path; | |
console.log(out); | |
fs.appendFile('output.csv', out + "\n", function (err) {}); | |
} catch (error) { | |
console.log(error); | |
} | |
} | |
} | |
doBatch('input.csv').then(res => {console.log("Done!");process.exit(0);}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment