Skip to content

Instantly share code, notes, and snippets.

@JpOnline
Created September 6, 2019 18:17
Show Gist options
  • Save JpOnline/e24c5ec870ff935a98459ca6546c6dd6 to your computer and use it in GitHub Desktop.
Save JpOnline/e24c5ec870ff935a98459ca6546c6dd6 to your computer and use it in GitHub Desktop.
// General e2e test that click random things in a page checking the
// events and state of a Re-Frame app.
//
// To Run
// npm i puppeteer
// node puppeteer-clicking-randomly.js
const puppeteer = require('puppeteer');
var fs = require('fs');
const TIMESTOCLICK = 30
async function createDir(){
const now = new Date()
const dir = './' + now.getFullYear() + "-"
+ (now.getMonth() + 1) + "-"
+ now.getDate() + "_"
+ now.getHours() + "-"
+ now.getMinutes() + "-"
+ now.getSeconds();
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
return dir
}
async function hideReFrame10xSidePanel(page){
await page.keyboard.down('Control');
await page.keyboard.press('KeyH');
await page.keyboard.up('Control');
}
async function takeFullPageScreenshot(page, filePath){
const bodyHandle = await page.$('body');
const { width, height } = await bodyHandle.boundingBox();
await page.screenshot({
clip: {
x: 0,
y: 0,
width,
height
},
path: filePath
});
await bodyHandle.dispose();
}
function redefineReFrameEventHandler() {
const temp = window.re_frame.events.handle.bind({})
window.re_frame.events.handle = reFrameEvent => {
window.pptr_i = window.pptr_i + 1 | 0
window.pptr_event = reFrameEvent
return temp(reFrameEvent);
}
}
async function reportDifferenceInAppState(page, appendToReport, mainFn){
// Keep the app-state
await page.evaluate('window.pptr_stateBefore = window.pptr_stateAfter || window.re_frame.db.app_db.state');
await mainFn()
// Difference from the last 2 app-states
await page.evaluate('window.pptr_stateAfter = window.re_frame.db.app_db.state');
const stateDiff = await page.evaluate(() => {
return cljs.core.clj__GT_js.call(
null,cljs.core.zipmap.call(
null,new cljs.core.PersistentVector(null, 2, 5,
cljs.core.PersistentVector.EMPTY_NODE, [
new cljs.core.Keyword(null,"before","before",-1633692388),
new cljs.core.Keyword(null,"after","after",594996914)
], null),
cljs.core.butlast.call(
null,clojure.data.diff.call(null,
window.pptr_stateBefore,
window.pptr_stateAfter))));
// Equivalent in cljs
// (->> (clojure.data/diff %1 %2)
// butlast
// (zipmap [:before :after])
// clj->js)
});
if (stateDiff.before !== null || stateDiff.after !== null){
appendToReport(JSON.stringify(stateDiff, null, 2))
}
}
async function scrollToElement(page, appendToReport, elementId){
try {
await page.hover('#'+elementId)
}catch(err){
if ((err.message == 'Node is either not visible or not an HTMLElement' ||
err.message == 'Node is detached from document')) {
appendToReport("Erro ao dar scroll: " + err.message + "\n")
}
}
}
function clickRandomElement(newId) {
// const testButton = document.querySelector('#teste')
// testButton.click()
const allElements = [];
const blockList = ['#--re-frame-10x--', '#com-rigsomelight-devcards-main > div > div > div.com-rigsomelight-devcards-card-base.com-rigsomelight-devcards-breadcrumbs.com-rigsomelight-devcards-typog', '#hide-all', '#show-all']
const findAllElements = function(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
allElements.push(el);
// If the element has a shadow root, dig deeper.
if (el.shadowRoot) {
findAllElements(el.shadowRoot.querySelectorAll('*'));
}
}
};
findAllElements(document.querySelectorAll('*'));
const elems = allElements
.filter(el => el.offsetParent !== null)
.filter(el => el.onclick !== null)
.filter(el => el.localName !== 'a' || el.href !== "#")
.filter(el => !isAChildOfABlockedListElement(el))
.filter(el => typeof(el.click) === 'function')
const randomElem = elems[Math.floor(Math.random() * elems.length)];
try{
randomElem.click()
}catch(err){
return "Erro ao clicar: " + err.message
}
randomElem.id = newId
return [randomElem.localName, randomElem.innerText.replace(/\n/g, "")]
function isAChildOfABlockedListElement(el){
for (b of blockList){
let blockedEl = document.querySelector(b)
if (blockedEl && blockedEl.contains(el)){
return true
}
}
return false
}
}
(async () => {
const browser = await puppeteer.launch({
// headless: false
});
const page = await browser.newPage()
const url = 'http://localhost:9500/cards.html#!/pr4.prototipo'
await page.goto(url)
const dir = createDir()
const appendToReport = async textToAppend => {
fs.appendFileSync((await dir) + "/report.txt", textToAppend);
}
appendToReport(url + "\n")
await hideReFrame10xSidePanel(page)
await page.evaluate(redefineReFrameEventHandler);
for (let i = 1; i <= TIMESTOCLICK; i++){
await reportDifferenceInAppState(page, appendToReport, async () => {
// Used to see if some event was dispatched
let eventIndexAfter
let eventIndexBefore = eventIndexAfter || await page.evaluate('window.pptr_i')
let elementId = "clicked" + i
const clickedElement = await page.evaluate(clickRandomElement, elementId)
await scrollToElement(page, appendToReport, elementId)
appendToReport("\n\n" + elementId + ": " + clickedElement + "\n")
console.log(elementId, clickedElement);
// Save screenshot every 10 clicks
if (i % 10 == 0){
await page.screenshot({path: await dir + '/' + elementId + '.png'});
}
if (i % 20 == 1){
await takeFullPageScreenshot(page, await dir + '/' + elementId + '_fullpage.png')
}
// await page.waitFor(1000)
// Used to see if some event was dispatched
eventIndexAfter = await page.evaluate('window.pptr_i')
// Append event to the report file only if an event was dispatched
if (eventIndexBefore !== eventIndexAfter){
let lastEvent = await page.evaluate('cljs.core.clj__GT_js.call(null, window.pptr_event)')
appendToReport("Evento: " + lastEvent + "\n")
console.log("Evento", lastEvent)
}
})
}
await browser.close()
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment