Created
June 22, 2018 20:22
-
-
Save martinschierle/c48a6fce06e010f5c4c3c1fb6c863dee to your computer and use it in GitHub Desktop.
New Perofrmance Metric - last time the CTA changed
This file contains 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'); | |
const pixelmatch = require('pixelmatch'); | |
const extractDomain = require('extract-domain'); | |
var fs = require('fs'); | |
//we'll simulate a nexus 5x for this | |
const phone = devices['Nexus 5X']; | |
/** | |
Will be called after page is loaded to find the CTA and return some info about it, | |
mainly last time it changed | |
*/ | |
function getResults() { | |
// go through all candidates, and find the one which comes first (smallest y coord) | |
// and fills at least 50% of screen width | |
for(var i = 0; i < window.ctaCandidates.length; i++) { | |
var node = window.ctaCandidates[i]; | |
if(!node || !node.parentNode) continue; | |
console.log("Considering node: " + node.innerText + " - " + node.id + " - " + node.name + " - " +node.getAttribute('class')); | |
var yThresh = window.ctaButton==null ? Number.MAX_SAFE_INTEGER : window.ctaButton.getBoundingClientRect().top | |
if(node.clientWidth > 0.5*window.innerWidth && node.getBoundingClientRect().top < yThresh) { | |
window.ctaButton = node; | |
} | |
} | |
if(window.ctaButton)console.log("Decided node: " + window.ctaButton.innerText + " - " + window.ctaButton.id + " - " + window.ctaButton.name + " - " +window.ctaButton.getAttribute('class')); | |
// return some general performance metric with our new metric | |
return { | |
"url": document.location.href, | |
"ctaLabel": window.ctaButton ? window.ctaButton.innerText: "n/a", | |
"ctaId": window.ctaButton ? window.ctaButton.id: "n/a", | |
"domInteractive": window.performance.timing.domInteractive - window.performance.timing.navigationStart, | |
"ctaComplate": window.ctaButton ? window.ctaButton.ctaLastChange - window.performance.timing.navigationStart: "n/a", | |
"domContentLoadedEventStart": window.performance.timing.domContentLoadedEventStart - window.performance.timing.navigationStart, | |
"loadEventStart": window.performance.timing.loadEventStart - window.performance.timing.navigationStart, | |
}; | |
} | |
/** | |
Let's make sure to find out when our cta candidates resized, and remember the time they did | |
*/ | |
function addResizeObserver() { | |
window.ro = new ResizeObserver( entries => { | |
for (let entry of entries) { | |
entry.target.ctaLastChange = Date.now(); | |
} | |
}); | |
} | |
/** | |
Some general things to init | |
*/ | |
function initCtaDetection() { | |
// small helper function to add a new candidate if it's eligible (buttons only for now) | |
// if we already know this cnadidate, adapt the timestamp of last change | |
// todo: think about clickable divs as CTAs | |
window.maybeAddCtaCandidate = function(n) { | |
if(!n) return; | |
if (n.tagName === 'BUTTON' || (n.tagName === 'INPUT' && n.type === 'submit')) { | |
if(!n.ctaLastChange) { | |
window.ctaCandidates.push(n); | |
window.ro.observe(n); | |
} | |
n.ctaLastChange = Date.now(); | |
} | |
}; | |
//in case the button is already totally stable, add all current buttons | |
if(!window.ctaCandidates) window.ctaCandidates = []; | |
var buttons = document.getElementsByTagName("BUTTON"); | |
for(var i = 0; i<buttons.length; i++) window.maybeAddCtaCandidate(buttons[i]); | |
buttons = document.querySelectorAll("[type='submit']"); | |
for(var i = 0; i<buttons.length; i++) window.maybeAddCtaCandidate(buttons[i]); | |
} | |
/** | |
The mutation observer - to find candidates for a CTA, and to trigger timestamp | |
updates for the ones we already identified whenever they are modified | |
*/ | |
function addMutationObserver() { | |
var mutationObserver = new MutationObserver(function(mutations) { | |
mutations.forEach(function(mutation) { | |
for (var i=0; i < mutations.length; i++){ | |
window.maybeAddCtaCandidate(mutations[i].target); | |
for (var j=0; j < mutations[i].addedNodes.length; j++){ | |
var n = mutations[i].addedNodes[j]; | |
window.maybeAddCtaCandidate(n); | |
} | |
} | |
}); | |
}); | |
mutationObserver.observe(document, { | |
attributes: true, | |
characterData: true, | |
childList: true, | |
subtree: true, | |
attributeOldValue: true, | |
characterDataOldValue: true | |
}); | |
} | |
async function findCTA(url) { | |
var domain = extractDomain(url); | |
const browser = await puppeteer.launch(); | |
const page = await browser.newPage(); | |
//page.on('console', msg => console.log(msg.text())); | |
page.on('error', err=> {console.log('error happen at the page: ', err);}); | |
page.on('pageerror', pageerr=> {console.log('pageerror occurred: ', pageerr);}); | |
await page.emulate(phone); | |
await page.evaluateOnNewDocument(initCtaDetection); | |
await page.evaluateOnNewDocument(addResizeObserver); | |
await page.evaluateOnNewDocument(addMutationObserver); | |
await page.goto(url, {"waitUntil": "networkidle0"}); | |
var results = await page.evaluate(getResults); | |
//screenshot page, helps with troubleshooting (look out for consent or country choser popups, etc.) | |
await page.screenshot({"path": domain+".jpg", "type": "jpeg"}); | |
// screenshot the cta - also helpful for debugging, to check if it's the right button | |
var cta = await page.evaluateHandle("window.ctaButton"); | |
cta = cta.asElement(); | |
if(cta) { | |
await cta.asElement().screenshot({"path": domain+"_cta.jpg", "type": "jpeg"}); | |
} | |
await browser.close(); | |
return results; | |
} | |
const testURLs = [ | |
"https://www.amazon.com/Intex-King-Inflatable-Lounge-Colors/dp/B00005O6B7?pd_rd_wg=e56B6&pd_rd_r=4d41d229-dfbc-449c-a983-8fad028d64c2&pd_rd_w=hJx7j&ref_=pd_gw_ri&pf_rd_r=KAVNYHEZ08THWK7RTJJ0&pf_rd_p=c116cecb-5676-58e0-b306-0894a1d0149e", | |
]; | |
for(var i in testURLs) { | |
findCTA(testURLs[i]).then(result => console.log(JSON.stringify(result, null, 4))); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment