const fs = require('fs');
const {chromium} = require('playwright');
const waitForNetworkSettled = require('./networkSettled');
const maths = require('./maths');
async function run(browser, config) {
const context = await browser.newContext({
screen: {width: 1920, height: 1080},
});
const page = await context.newPage();
const client = await page.context().newCDPSession(page)
await client.send('Network.enable')
await client.send('Network.emulateNetworkConditions', {
offline: false,
downloadThroughput: (4 * 1024 * 1024) / 8,
uploadThroughput: (4 * 1024 * 1024) / 8,
latency: 20
})
if (config.runType === "native") {
await page.route(/.*?adequa\.js/, route => route.abort());
await page.route(/.*?potions\.js/, route => route.abort());
}
await page.goto(config.pageUrl, {waituntil: 'domcontentloaded'});
if(config.cookieBannerAcceptSelector) {
await waitForNetworkSettled(page, async () => {
await page.click(config.cookieBannerAcceptSelector);
});
}
const start = Date.now();
if(config.runType === "potions") {
await page.waitForFunction(() => {
try {
return !!adequa.widgets.products.listeners[0];
} catch (e) {
return false
}
});
}
await page.click(config.addToCartButtonSelector);
await page.waitForSelector(
config.runType === "potions" ? config.potionsBannerSelector : config.nativeBannerSelector);
const time = Date.now() - start;
await context.close();
return time;
}
(async (config) => {
const browser = await chromium.launch({headless:false});
const runTiming = [];
for (const i of Array(config.numberOfRuns).keys()) {
console.log(`${i + 1}/${config.numberOfRuns}`)
runTiming.push(await run(browser, config));
}
await browser.close();
fs.writeFileSync(
`results/${config.pageUrl
.replace("https:", "")
.replaceAll("/", "")
.replaceAll(".", "")}_domloaded_${config.runType}`,
JSON.stringify({
min: Math.min(...runTiming),
max: Math.max(...runTiming),
mean: maths.mean(runTiming),
median: maths.median(runTiming),
p75: maths.quantile(runTiming, .75),
p95: maths.quantile(runTiming, .95),
p99: maths.quantile(runTiming, .99),
raw: runTiming
}));
})({
pageUrl: "https://www.alltricks.fr/",
cookieBannerAcceptSelector: "#didomi-notice-agree-button",
addToCartButtonSelector: "form > div > button",
potionsBannerSelector: "#adq_products",
nativeBannerSelector: ".allbox-cart-back:not(.mfp-hide) .recommendedProductModule .alltricks-Product--3columns",
runType: "potions",
numberOfRuns: 10
});
module.exports = async function waitForNetworkSettled(page, action, longPolls = 0) {
let networkSettledCallback;
const networkSettledPromise = new Promise(f => networkSettledCallback = f);
let requestCounter = 0;
let actionDone = false;
const maybeSettle = () => {
if (actionDone && requestCounter <= longPolls)
networkSettledCallback();
};
const onRequest = () => {
++requestCounter;
};
const onRequestDone = () => {
const evaluate = page.evaluate(() => new Promise(f => setTimeout(f, 0)));
evaluate.catch(e => null).then(() => {
--requestCounter;
maybeSettle();
});
};
page.on('request', onRequest);
page.on('requestfinished', onRequestDone);
page.on('requestfailed', onRequestDone);
const result = await action();
actionDone = true;
maybeSettle();
await networkSettledPromise;
page.removeListener('request', onRequest);
page.removeListener('requestfinished', onRequestDone);
page.removeListener('requestfailed', onRequestDone);
return result;
};
const asc = arr => arr.sort((a, b) => a - b);
const sum = arr => arr.reduce((a, b) => a + b, 0);
const mean = arr => sum(arr) / arr.length;
const quantile = (arr, q) => {
const sorted = asc(arr);
const pos = (sorted.length - 1) * q;
const base = Math.floor(pos);
const rest = pos - base;
if (sorted[base + 1] !== undefined) {
return sorted[base] + rest * (sorted[base + 1] - sorted[base]);
} else {
return sorted[base];
}
};
const median = arr => quantile(arr, .50);
module.exports = {
asc,
sum,
mean,
quantile,
median
}