From 082409d6109f87060d7b993823fc31c587fa1c48 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 27 Mar 2024 17:32:38 +0100 Subject: [PATCH 01/14] Updated to shelljs --- package-lock.json | 164 +++++++++++++++++++++++++++++++++++- package.json | 3 +- src/currentToPower/index.js | 55 ++++++------ 3 files changed, 192 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2de00ce..b73b42f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "dependencies": { "decompress": "^4.2.1", - "selenium-webdriver": "^4.7.1" + "selenium-webdriver": "^4.7.1", + "shelljs": "^0.8.5" }, "devDependencies": { "eslint": "^8.33.0", @@ -786,6 +787,14 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", @@ -864,6 +873,17 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -936,6 +956,25 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1225,6 +1264,11 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -1342,6 +1386,17 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -1354,6 +1409,22 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1466,6 +1537,22 @@ "node": ">=8" } }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1518,6 +1605,17 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", @@ -2257,6 +2355,11 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", @@ -2314,6 +2417,14 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2360,6 +2471,19 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2584,6 +2708,11 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -2660,12 +2789,30 @@ "util-deprecate": "~1.0.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "requires": { + "resolve": "^1.1.6" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2738,6 +2885,16 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2778,6 +2935,11 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", diff --git a/package.json b/package.json index 3c69a1b..ac3809d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ }, "dependencies": { "decompress": "^4.2.1", - "selenium-webdriver": "^4.7.1" + "selenium-webdriver": "^4.7.1", + "shelljs": "^0.8.5" }, "devDependencies": { "eslint": "^8.33.0", diff --git a/src/currentToPower/index.js b/src/currentToPower/index.js index 72611b2..95ca762 100644 --- a/src/currentToPower/index.js +++ b/src/currentToPower/index.js @@ -1,31 +1,29 @@ import fs from 'fs'; import path from 'path'; -import { createFileList } from '../helpers.js'; +import shell from 'shelljs'; const output = []; const VOLTAGE = 230; const numberToFixedNumber = (number, digits) => Number(number.toFixed(digits)); -const paths = [ - ...createFileList('output/current/idle'), - ...createFileList('output/current/idleWithBrowser'), - ...createFileList('output/current/withGhostery'), - ...createFileList('output/current/withoutGhostery'), -]; - -paths.forEach((filePath, k) => { - const measurments = fs - .readFileSync(filePath, { encoding: 'utf8' }) - .split(/\r?\n/) - .map((l) => l.split('\t')) - .map((m) => [m[0], Number(m[1]) * VOLTAGE]); - - const powerAverage = powerByAverage(measurments); - const powerIntegral = powerByIntegral(measurments); - const duration = durationInMinutes(measurments); - - /* +const directories = shell.ls('-d', 'output/current/*/'); + +directories.forEach((dir) => { + const files = fs.readdirSync(dir); + files.forEach((file, k) => { + const filePath = path.join(dir, file); + const measurments = fs + .readFileSync(filePath, { encoding: 'utf8' }) + .split(/\r?\n/) + .map((l) => l.split('\t')) + .map((m) => [m[0], Number(m[1]) * VOLTAGE]); + + const powerAverage = powerByAverage(measurments); + const powerIntegral = powerByIntegral(measurments); + const duration = durationInMinutes(measurments); + + /* AVG unit is a watts INTEGRAL unit is a watts AVG_CTP (current to power) unit is a watt-hour @@ -33,14 +31,15 @@ paths.forEach((filePath, k) => { duration is in minutes */ - output.push({ - collectionOfCurrentSample: k + 1, - fileName: path.basename(filePath), - AVG: numberToFixedNumber(powerAverage, 3), - AVG_C2P: numberToFixedNumber(powerAverage * (duration / 60), 3), - INTEGRAL: numberToFixedNumber(powerIntegral, 3), - INTEGRAL_C2P: numberToFixedNumber(powerIntegral * (duration / 60), 3), - duration: numberToFixedNumber(duration, 3), + output.push({ + collectionOfCurrentSample: k + 1, + fileName: path.basename(filePath), + AVG: numberToFixedNumber(powerAverage, 3), + AVG_C2P: numberToFixedNumber(powerAverage * (duration / 60), 3), + INTEGRAL: numberToFixedNumber(powerIntegral, 3), + INTEGRAL_C2P: numberToFixedNumber(powerIntegral * (duration / 60), 3), + duration: numberToFixedNumber(duration, 3), + }); }); }); From 05a8e2076091e069bf022fc8fb89405028e218a6 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Fri, 29 Mar 2024 17:28:27 +0100 Subject: [PATCH 02/14] Prepare for installing uBO --- .gitignore | 2 ++ index.js | 86 ++++++++++++++++++++++++++++++++++++-------------- src/helpers.js | 18 +++++++---- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index de4e42a..dfa8332 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,10 @@ output/current/withGhostery/** output/current/withoutGhostery/** output/time/withGhostery/** output/time/withoutGhostery/** +output/time/withUBlockOrigin/** profiles/withGhostery/** profiles/withoutGhostery/** +profiles/withUBlockOrigin/** # Do not ignore a special file name !.gitkeep diff --git a/index.js b/index.js index b09ca1f..f174b6c 100644 --- a/index.js +++ b/index.js @@ -13,23 +13,46 @@ const isChromeSelected = Boolean( const isFirefoxSelected = Boolean( process.argv.find((arg) => arg === '--firefox'), ); -const isGhosteryEnabled = Boolean( - process.argv.find((arg) => arg === '--with-ghostery'), -); +let selectedExtension = { + isGhostery: Boolean(process.argv.find((arg) => arg === '--with-ghostery')), + isUBlockOrigin: Boolean(process.argv.find((arg) => arg === '--with-uBO')), +}; + +const extensionUrls = { + Firefox: { + Ghostery: + 'https://github.com/ghostery/ghostery-extension/releases/download/v10.2.10/ghostery-firefox.zip', + UBlockOrigin: + 'https://github.com/gorhill/uBlock/releases/download/1.56.0/uBlock0_1.56.0.firefox.signed.xpi', + }, + Chrome: { + Ghostery: + 'https://github.com/ghostery/ghostery-extension/releases/download/v10.2.10/ghostery-chrome.zip', + UBlockOrigin: + 'https://github.com/gorhill/uBlock/releases/download/1.56.0/uBlock0_1.56.0.chromium.zip', + }, +}; + +const browser = isFirefoxSelected ? 'Firefox' : 'Chrome'; +let extensionUrl; + +if (selectedExtension.isGhostery) { + extensionUrl = extensionUrls[browser]['Ghostery']; +} else if (selectedExtension.isUBlockOrigin) { + extensionUrl = extensionUrls[browser]['UBlockOrigin']; +} const config = isFirefoxSelected ? { - browser: 'Firefox', + browser: browser, addonUUID: 'd56a5b99-51b6-4e83-ab23-796216679614', - extensionUrl: - 'https://github.com/ghostery/ghostery-extension/releases/download/v10.2.10/ghostery-firefox.zip', + extensionUrl: extensionUrl, addonBaseUrl: `moz-extension://d56a5b99-51b6-4e83-ab23-796216679614`, } : { - browser: 'Chrome', + browser: browser, addonUUID: null, - extensionUrl: - 'https://github.com/ghostery/ghostery-extension/releases/download/v10.2.10/ghostery-chrome.zip', + extensionUrl: extensionUrl, addonBaseUrl: null, //get from runtime }; @@ -51,22 +74,33 @@ const urls = fs let outputPath = 'output/time'; let driver; -const addon = await downloadAddon(config.extensionUrl); +const addon = await downloadAddon(config.extensionUrl, selectedExtension); + +let profileOutputPath = ''; +if (selectedExtension.isGhostery) { + profileOutputPath = `profiles/withGhostery/${config.browser}`; +} else if (selectedExtension.isUBlockOrigin) { + profileOutputPath = `profiles/withUBlockOrigin/${config.browser}`; +} else { + profileOutputPath = `profiles/withoutGhostery/${config.browser}`; +} if (isChromeSelected) { const options = new chrome.Options(); options.addArguments('--profile-directory=Default'); - if (isGhosteryEnabled) { + if (selectedExtension.isGhostery) { console.log(`LOG: Installing addon for ${config.browser}.`); - options.addArguments( - `--user-data-dir=profiles/withGhostery/${config.browser}`, - ); + options.addArguments(`--user-data-dir=${profileOutputPath}`); outputPath += `/withGhostery/${config.browser}`; options.addArguments(`--load-extension=${addon}`); + } else if (selectedExtension.isUBlockOrigin) { + console.log(`LOG: Installing addon for ${config.browser}.`); + options.addArguments(`--user-data-dir=${profileOutputPath}`); + outputPath += `/withUBlockOrigin/${config.browser}`; + options.addArguments(`--load-extension=${addon}/uBlock0.chromium`); + // options.addExtensions(addon); } else { - options.addArguments( - `--user-data-dir=profiles/withoutGhostery/${config.browser}`, - ); + options.addArguments(`--user-data-dir=${profileOutputPath}`); outputPath += `/withoutGhostery/${config.browser}`; } driver = await new Builder() @@ -80,9 +114,15 @@ if (isChromeSelected) { `{"firefox@ghostery.com": "${config.addonUUID}"}`, ); // options.addArguments("--headless"); - if (isGhosteryEnabled) { + if (selectedExtension.isGhostery) { options.addArguments('-profile', `profiles/withGhostery/${config.browser}`); outputPath += `/withGhostery/${config.browser}`; + } else if (selectedExtension.isUBlockOrigin) { + options.addArguments( + '-profile', + `profiles/withUBlockOrigin/${config.browser}`, + ); + outputPath += `/withGhostery/${config.browser}`; } else { options.addArguments( '-profile', @@ -95,7 +135,7 @@ if (isChromeSelected) { .setFirefoxOptions(options) .build(); - if (isGhosteryEnabled) { + if (selectedExtension.isGhostery || selectedExtension.isUBlockOrigin) { console.log(`LOG: Installing addon for ${config.browser}.`); await driver.installAddon(addon, true); } @@ -151,7 +191,7 @@ const logPageLoadTime = async (n, url, now) => { }; try { - if (isGhosteryEnabled) { + if (selectedExtension.isGhostery) { if (!fs.existsSync(`profiles/withGhostery/${config.browser}/onboarded`)) { await driver.wait( async () => (await driver.getAllWindowHandles()).length === 2, @@ -201,15 +241,13 @@ try { } fs.writeFileSync(`profiles/withGhostery/${config.browser}/onboarded`, ''); - } - - console.info(`INFO: Open websites from for region: ${region}.`); - if (isGhosteryEnabled) { // Wait for Ghostery extension to download fresh Ad-blocking filters await sleep(1000 * 20); } + console.info(`INFO: Open websites from for region: ${region}.`); + await driver.executeScript('window.open()', ''); const handle = await driver.getAllWindowHandles(); await driver.switchTo().window(handle[0]); diff --git a/src/helpers.js b/src/helpers.js index 94d0c51..953b5a2 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -16,21 +16,24 @@ export const switchToWindowWithUrl = async (driver, url) => { } }; -export const downloadAddon = async (url) => { +export const downloadAddon = async (url, selectedExtension) => { const hash = crypto.createHash('md5').update(url).digest('hex'); const tempPath = fs.mkdtempSync( - path.join(os.tmpdir(), 'ghostery-benchmarks'), + path.join(os.tmpdir(), 'extension-benchmarks'), ); console.log('LOG: Addon temp path:', tempPath); - let addonFilePath = path.join(tempPath, `${hash}.zip`); - let addonPath = path.join(tempPath, hash); - + let extension = selectedExtension.isUBlockOriginEnabled ? '.xpi' : '.zip'; if (url.endsWith('zip')) { console.log('LOG: ZIP file'); + } else if (url.endsWith('xpi')) { + console.log('LOG: XPI file'); } + let addonFilePath = path.join(tempPath, `${hash}${extension}`); + let addonPath = path.join(tempPath, hash); + if (!fs.existsSync(addonFilePath)) { console.log('LOG: Downloading addon'); const res = await fetch(url); @@ -40,7 +43,10 @@ export const downloadAddon = async (url) => { fs.writeFileSync(addonFilePath, buffer); } - if (!fs.existsSync(addonPath) && url.endsWith('zip')) { + if ( + !fs.existsSync(addonPath) && + (url.endsWith('zip') || url.endsWith('xpi')) + ) { console.log('LOG: Unpacking addon'); await decompress(addonFilePath, addonPath); } From dbde1851e882b025e6428c6910c0464cd8551500 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 3 Apr 2024 17:21:58 +0200 Subject: [PATCH 03/14] Added uBO --- README.md | 1 + index.js | 27 +++++++++++++-------------- output/time/withUBlockOrigin/.gitkeep | 0 profiles/withUBlockOrigin/.gitkeep | 0 4 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 output/time/withUBlockOrigin/.gitkeep create mode 100644 profiles/withUBlockOrigin/.gitkeep diff --git a/README.md b/README.md index 7fc0f33..15128f8 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Usage: --firefox select Firefox browser --chrome select Chrome browser --with-ghostery load Ghostery extension +--with-uBO load uBlock Origin extension ``` Example output: diff --git a/index.js b/index.js index f174b6c..9ed3b7a 100644 --- a/index.js +++ b/index.js @@ -7,12 +7,10 @@ import { downloadAddon, sleep, switchToWindowWithUrl } from './src/helpers.js'; const timestamp = new Date().toISOString(); const isRegionEU = Boolean(process.argv.find((arg) => arg === '--EU')); const isRegionUS = Boolean(process.argv.find((arg) => arg === '--US')); -const isChromeSelected = Boolean( - process.argv.find((arg) => arg === '--chrome'), -); -const isFirefoxSelected = Boolean( - process.argv.find((arg) => arg === '--firefox'), -); +let selectedBrowser = { + isChrome: Boolean(process.argv.find((arg) => arg === '--chrome')), + isFirefox: Boolean(process.argv.find((arg) => arg === '--firefox')), +}; let selectedExtension = { isGhostery: Boolean(process.argv.find((arg) => arg === '--with-ghostery')), isUBlockOrigin: Boolean(process.argv.find((arg) => arg === '--with-uBO')), @@ -33,7 +31,7 @@ const extensionUrls = { }, }; -const browser = isFirefoxSelected ? 'Firefox' : 'Chrome'; +const browser = selectedBrowser.isFirefox ? 'Firefox' : 'Chrome'; let extensionUrl; if (selectedExtension.isGhostery) { @@ -42,7 +40,7 @@ if (selectedExtension.isGhostery) { extensionUrl = extensionUrls[browser]['UBlockOrigin']; } -const config = isFirefoxSelected +const config = selectedBrowser.isFirefox ? { browser: browser, addonUUID: 'd56a5b99-51b6-4e83-ab23-796216679614', @@ -85,7 +83,7 @@ if (selectedExtension.isGhostery) { profileOutputPath = `profiles/withoutGhostery/${config.browser}`; } -if (isChromeSelected) { +if (selectedBrowser.isChrome) { const options = new chrome.Options(); options.addArguments('--profile-directory=Default'); if (selectedExtension.isGhostery) { @@ -98,7 +96,6 @@ if (isChromeSelected) { options.addArguments(`--user-data-dir=${profileOutputPath}`); outputPath += `/withUBlockOrigin/${config.browser}`; options.addArguments(`--load-extension=${addon}/uBlock0.chromium`); - // options.addExtensions(addon); } else { options.addArguments(`--user-data-dir=${profileOutputPath}`); outputPath += `/withoutGhostery/${config.browser}`; @@ -107,13 +104,12 @@ if (isChromeSelected) { .forBrowser(Browser.CHROME) .setChromeOptions(options) .build(); -} else if (isFirefoxSelected) { +} else if (selectedBrowser.isFirefox) { const options = new firefox.Options(); options.setPreference( 'extensions.webextensions.uuids', `{"firefox@ghostery.com": "${config.addonUUID}"}`, ); - // options.addArguments("--headless"); if (selectedExtension.isGhostery) { options.addArguments('-profile', `profiles/withGhostery/${config.browser}`); outputPath += `/withGhostery/${config.browser}`; @@ -198,7 +194,7 @@ try { ); console.info('INFO: Ghostery onboarding opened.'); - if (isChromeSelected) { + if (selectedBrowser.isChrome) { let handles = await driver.getAllWindowHandles(); await driver.switchTo().window(handles[1]); config.addonBaseUrl = (await driver.getCurrentUrl()).split('/pages')[0]; @@ -244,6 +240,9 @@ try { // Wait for Ghostery extension to download fresh Ad-blocking filters await sleep(1000 * 20); + console.info('INFO: Ghostery - Privacy Ad Blocker was installed.'); + } else if (selectedExtension.isUBlockOrigin) { + console.info('INFO: uBlock Origin was installed.'); } console.info(`INFO: Open websites from for region: ${region}.`); @@ -261,7 +260,7 @@ try { } if ( - isChromeSelected && + selectedBrowser.isChrome && fs.existsSync(`profiles/withGhostery/${config.browser}/onboarded`) ) { fs.unlinkSync(`profiles/withGhostery/${config.browser}/onboarded`); diff --git a/output/time/withUBlockOrigin/.gitkeep b/output/time/withUBlockOrigin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/profiles/withUBlockOrigin/.gitkeep b/profiles/withUBlockOrigin/.gitkeep new file mode 100644 index 0000000..e69de29 From 33e531ea5a73246eaf2c9348fe6b66dc2c4fc073 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Thu, 4 Apr 2024 13:17:17 +0200 Subject: [PATCH 04/14] OutputPath fix + check if any addon has been selected --- .gitignore | 4 ++-- index.js | 10 +++++----- .../{withoutGhostery => withoutExtensions}/.gitkeep | 0 .../{withoutGhostery => withoutExtensions}/.gitkeep | 0 src/helpers.js | 4 ++++ 5 files changed, 11 insertions(+), 7 deletions(-) rename output/time/{withoutGhostery => withoutExtensions}/.gitkeep (100%) rename profiles/{withoutGhostery => withoutExtensions}/.gitkeep (100%) diff --git a/.gitignore b/.gitignore index dfa8332..1012cbb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,10 @@ output/current/idleWithBrowser/** output/current/withGhostery/** output/current/withoutGhostery/** output/time/withGhostery/** -output/time/withoutGhostery/** +output/time/withoutExtensions/** output/time/withUBlockOrigin/** profiles/withGhostery/** -profiles/withoutGhostery/** +profiles/withoutExtensions/** profiles/withUBlockOrigin/** # Do not ignore a special file name diff --git a/index.js b/index.js index 9ed3b7a..f532d5d 100644 --- a/index.js +++ b/index.js @@ -80,7 +80,7 @@ if (selectedExtension.isGhostery) { } else if (selectedExtension.isUBlockOrigin) { profileOutputPath = `profiles/withUBlockOrigin/${config.browser}`; } else { - profileOutputPath = `profiles/withoutGhostery/${config.browser}`; + profileOutputPath = `profiles/withoutExtensions/${config.browser}`; } if (selectedBrowser.isChrome) { @@ -98,7 +98,7 @@ if (selectedBrowser.isChrome) { options.addArguments(`--load-extension=${addon}/uBlock0.chromium`); } else { options.addArguments(`--user-data-dir=${profileOutputPath}`); - outputPath += `/withoutGhostery/${config.browser}`; + outputPath += `/withoutExtensions/${config.browser}`; } driver = await new Builder() .forBrowser(Browser.CHROME) @@ -118,13 +118,13 @@ if (selectedBrowser.isChrome) { '-profile', `profiles/withUBlockOrigin/${config.browser}`, ); - outputPath += `/withGhostery/${config.browser}`; + outputPath += `/withUBlockOrigin/${config.browser}`; } else { options.addArguments( '-profile', - `profiles/withoutGhostery/${config.browser}`, + `profiles/withoutExtensions/${config.browser}`, ); - outputPath += `/withoutGhostery/${config.browser}`; + outputPath += `/withoutExtensions/${config.browser}`; } driver = await new Builder() .forBrowser(Browser.FIREFOX) diff --git a/output/time/withoutGhostery/.gitkeep b/output/time/withoutExtensions/.gitkeep similarity index 100% rename from output/time/withoutGhostery/.gitkeep rename to output/time/withoutExtensions/.gitkeep diff --git a/profiles/withoutGhostery/.gitkeep b/profiles/withoutExtensions/.gitkeep similarity index 100% rename from profiles/withoutGhostery/.gitkeep rename to profiles/withoutExtensions/.gitkeep diff --git a/src/helpers.js b/src/helpers.js index 953b5a2..49bc63e 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -17,6 +17,10 @@ export const switchToWindowWithUrl = async (driver, url) => { }; export const downloadAddon = async (url, selectedExtension) => { + if (!url) { + console.error('INFO: No extension selected.'); + return; + } const hash = crypto.createHash('md5').update(url).digest('hex'); const tempPath = fs.mkdtempSync( path.join(os.tmpdir(), 'extension-benchmarks'), From a11e17a53bd24d191f73e53b9140ecce12fe4b4c Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Thu, 4 Apr 2024 14:50:43 +0200 Subject: [PATCH 05/14] Added possibility to create or delete folders for output/time and profiles --- index.js | 33 ++++++++++++++++++++++++++++++++- profiles/withGhostery/.gitkeep | 0 src/helpers.js | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) delete mode 100644 profiles/withGhostery/.gitkeep diff --git a/index.js b/index.js index f532d5d..1c12dc5 100644 --- a/index.js +++ b/index.js @@ -2,11 +2,20 @@ import { Builder, Browser, By, until } from 'selenium-webdriver'; import firefox from 'selenium-webdriver/firefox.js'; import chrome from 'selenium-webdriver/chrome.js'; import fs from 'fs'; -import { downloadAddon, sleep, switchToWindowWithUrl } from './src/helpers.js'; +import { + downloadAddon, + sleep, + switchToWindowWithUrl, + createFolders, + deleteFolders, +} from './src/helpers.js'; const timestamp = new Date().toISOString(); const isRegionEU = Boolean(process.argv.find((arg) => arg === '--EU')); const isRegionUS = Boolean(process.argv.find((arg) => arg === '--US')); +const shouldDeleteFolders = Boolean( + process.argv.find((arg) => arg === '--delete-folders'), +); let selectedBrowser = { isChrome: Boolean(process.argv.find((arg) => arg === '--chrome')), isFirefox: Boolean(process.argv.find((arg) => arg === '--firefox')), @@ -16,6 +25,28 @@ let selectedExtension = { isUBlockOrigin: Boolean(process.argv.find((arg) => arg === '--with-uBO')), }; +const directoriesOutput = [ + './output/time/withGhostery/Firefox', + './output/time/withGhostery/Chrome', + './output/time/withUBlockOrigin/Firefox', + './output/time/withUBlockOrigin/Chrome', + './output/time/withoutExtensions/Firefox', + './output/time/withoutExtensions/Chrome', +]; + +const directoriesProfiles = [ + './profiles/withGhostery/Firefox', + './profiles/withGhostery/Chrome', + './profiles/withUBlockOrigin/Firefox', + './profiles/withUBlockOrigin/Chrome', + './profiles/withoutExtensions/Firefox', + './profiles/withoutExtensions/Chrome', +]; + +createFolders(directoriesOutput); +createFolders(directoriesProfiles); +deleteFolders(directoriesProfiles, shouldDeleteFolders); + const extensionUrls = { Firefox: { Ghostery: diff --git a/profiles/withGhostery/.gitkeep b/profiles/withGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/helpers.js b/src/helpers.js index 49bc63e..2c4ddd6 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -3,6 +3,7 @@ import path from 'node:path'; import os from 'node:os'; import crypto from 'node:crypto'; import decompress from 'decompress'; +import shell from 'shelljs'; export const sleep = (ms) => new Promise((r) => setTimeout(() => r(), ms)); @@ -69,3 +70,27 @@ export const createFileList = (folderPath) => { } return paths; }; + +export const createFolders = (directories) => { + directories.forEach((dir) => { + if (!shell.test('-d', dir)) { + shell.mkdir('-p', dir); + console.log(`INFO: Directory '${dir}' - is created.`); + } else { + console.log(`INFO: Directory '${dir}' - already exists.`); + } + }); +}; + +export const deleteFolders = (directories, shouldDeleteFolders) => { + if (shouldDeleteFolders) { + directories.forEach((dir) => { + if (shell.test('-d', dir)) { + shell.rm('-rf', dir); + console.log(`INFO: Directory '${dir}' - is deleted.`); + } else { + console.log(`INFO: Directory '${dir}' - does not exist.`); + } + }); + } +}; From 3f5398134fefd3a25c0a3c0bf92a7b58eaa28651 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Thu, 4 Apr 2024 15:02:45 +0200 Subject: [PATCH 06/14] Change in creating / deleting folders --- README.md | 14 ++++++++------ index.js | 14 +++++++++++--- src/helpers.js | 20 +++++++++----------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 15128f8..a3dfcf2 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,14 @@ npm start Usage: ``` ---US site list for US region ---EU site list for European region ---firefox select Firefox browser ---chrome select Chrome browser ---with-ghostery load Ghostery extension ---with-uBO load uBlock Origin extension +--US site list for US region +--EU site list for European region +--firefox select Firefox browser +--chrome select Chrome browser +--with-ghostery load Ghostery extension +--with-uBO load uBlock Origin extension +--delete-profiles-folders delete browser profile folders +--delete-output-folders delete output folders ``` Example output: diff --git a/index.js b/index.js index 1c12dc5..2f268cb 100644 --- a/index.js +++ b/index.js @@ -13,8 +13,11 @@ import { const timestamp = new Date().toISOString(); const isRegionEU = Boolean(process.argv.find((arg) => arg === '--EU')); const isRegionUS = Boolean(process.argv.find((arg) => arg === '--US')); -const shouldDeleteFolders = Boolean( - process.argv.find((arg) => arg === '--delete-folders'), +const deleteProfilesFolders = Boolean( + process.argv.find((arg) => arg === '--delete-profiles-folders'), +); +const deleteOutputsFolders = Boolean( + process.argv.find((arg) => arg === '--delete-output-folders'), ); let selectedBrowser = { isChrome: Boolean(process.argv.find((arg) => arg === '--chrome')), @@ -45,7 +48,12 @@ const directoriesProfiles = [ createFolders(directoriesOutput); createFolders(directoriesProfiles); -deleteFolders(directoriesProfiles, shouldDeleteFolders); +if (deleteProfilesFolders) { + deleteFolders(directoriesProfiles); +} +if (deleteOutputsFolders) { + deleteFolders(directoriesOutput); +} const extensionUrls = { Firefox: { diff --git a/src/helpers.js b/src/helpers.js index 2c4ddd6..44d7409 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -82,15 +82,13 @@ export const createFolders = (directories) => { }); }; -export const deleteFolders = (directories, shouldDeleteFolders) => { - if (shouldDeleteFolders) { - directories.forEach((dir) => { - if (shell.test('-d', dir)) { - shell.rm('-rf', dir); - console.log(`INFO: Directory '${dir}' - is deleted.`); - } else { - console.log(`INFO: Directory '${dir}' - does not exist.`); - } - }); - } +export const deleteFolders = (directories) => { + directories.forEach((dir) => { + if (shell.test('-d', dir)) { + shell.rm('-rf', dir); + console.log(`INFO: Directory '${dir}' - is deleted.`); + } else { + console.log(`INFO: Directory '${dir}' - does not exist.`); + } + }); }; From bcfa8d5725771c51cf931e7c9a15615c672f5b9c Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Mon, 8 Apr 2024 18:13:19 +0200 Subject: [PATCH 07/14] Chromedriver update --- package-lock.json | 945 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 930 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index b73b42f..bb5ec5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "@ghostery/benchmarks", "version": "1.0.0", "dependencies": { + "chromedriver": "^123.0.1", "decompress": "^4.2.1", "selenium-webdriver": "^4.7.1", "shelljs": "^0.8.5" @@ -109,6 +110,34 @@ "node": ">= 8" } }, + "node_modules/@testim/chrome-version": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", + "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + }, + "node_modules/@types/node": { + "version": "20.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", + "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "optional": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -130,6 +159,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -176,6 +216,32 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -200,6 +266,14 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -293,6 +367,27 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chromedriver": { + "version": "123.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz", + "integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==", + "hasInstallScript": true, + "dependencies": { + "@testim/chrome-version": "^1.1.4", + "axios": "^1.6.7", + "compare-versions": "^6.1.0", + "extract-zip": "^2.0.1", + "proxy-agent": "^6.4.0", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.2" + }, + "bin": { + "chromedriver": "bin/chromedriver" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -311,11 +406,27 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/compare-versions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", + "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -340,11 +451,18 @@ "node": ">= 8" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "engines": { + "node": ">= 14" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -449,8 +567,28 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } }, "node_modules/doctrine": { "version": "3.0.0", @@ -484,6 +622,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.33.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", @@ -639,6 +797,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -667,7 +837,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -676,11 +845,43 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -777,11 +978,56 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -807,6 +1053,20 @@ "node": ">=0.10.0" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -884,6 +1144,30 @@ "node": ">= 0.4" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -964,6 +1248,26 @@ "node": ">= 0.10" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -1018,6 +1322,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "node_modules/is2": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", + "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "dependencies": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + }, + "engines": { + "node": ">=v0.10.0" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1051,6 +1373,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1063,6 +1390,17 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -1116,6 +1454,14 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -1135,6 +1481,25 @@ "node": ">=4" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1149,8 +1514,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -1158,6 +1522,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1221,6 +1593,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -1343,6 +1745,38 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -1553,6 +1987,55 @@ "node": ">=4" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1633,6 +2116,31 @@ "node": ">= 0.8.0" } }, + "node_modules/tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "dependencies": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "node_modules/tcp-port-used/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -1660,6 +2168,11 @@ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1693,6 +2206,20 @@ "through": "^2.3.8" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "optional": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1853,6 +2380,34 @@ "fastq": "^1.6.0" } }, + "@testim/chrome-version": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", + "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==" + }, + "@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + }, + "@types/node": { + "version": "20.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", + "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "optional": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -1866,6 +2421,14 @@ "dev": true, "requires": {} }, + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "requires": { + "debug": "^4.3.4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1899,6 +2462,29 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "requires": { + "tslib": "^2.0.1" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1909,6 +2495,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==" + }, "bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -1976,6 +2567,20 @@ "supports-color": "^7.1.0" } }, + "chromedriver": { + "version": "123.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz", + "integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==", + "requires": { + "@testim/chrome-version": "^1.1.4", + "axios": "^1.6.7", + "compare-versions": "^6.1.0", + "extract-zip": "^2.0.1", + "proxy-agent": "^6.4.0", + "proxy-from-env": "^1.1.0", + "tcp-port-used": "^1.0.2" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1991,11 +2596,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "compare-versions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", + "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2017,11 +2635,15 @@ "which": "^2.0.1" } }, + "data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -2101,8 +2723,22 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "requires": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "doctrine": { "version": "3.0.0", @@ -2127,6 +2763,17 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, "eslint": { "version": "8.33.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", @@ -2234,6 +2881,11 @@ "eslint-visitor-keys": "^3.3.0" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -2255,14 +2907,33 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + } + } }, "fast-deep-equal": { "version": "3.1.3", @@ -2345,11 +3016,36 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2369,6 +3065,17 @@ "pinkie-promise": "^2.0.0" } }, + "get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "requires": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + } + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2425,6 +3132,24 @@ "function-bind": "^1.1.2" } }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2476,6 +3201,20 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, + "ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + } + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -2515,6 +3254,21 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "is2": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", + "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "requires": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2541,6 +3295,11 @@ "argparse": "^2.0.1" } }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2553,6 +3312,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -2597,6 +3365,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" + }, "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -2612,6 +3385,19 @@ } } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2623,8 +3409,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", @@ -2632,6 +3417,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2677,6 +3467,30 @@ "p-limit": "^3.0.2" } }, + "pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "requires": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + } + }, + "pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "requires": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + } + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -2763,6 +3577,35 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "requires": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2895,6 +3738,41 @@ "rechoir": "^0.6.2" } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "requires": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "requires": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.7.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2954,6 +3832,25 @@ "xtend": "^4.0.0" } }, + "tcp-port-used": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", + "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "requires": { + "debug": "4.3.1", + "is2": "^2.0.6" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2978,6 +3875,11 @@ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3002,6 +3904,17 @@ "through": "^2.3.8" } }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "optional": true + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index ac3809d..40b7224 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "lint": "eslint *.js" }, "dependencies": { + "chromedriver": "^123.0.1", "decompress": "^4.2.1", "selenium-webdriver": "^4.7.1", "shelljs": "^0.8.5" From 122098a733b2debac4cd331a58bcd18dc3112e2f Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Mon, 8 Apr 2024 18:17:03 +0200 Subject: [PATCH 08/14] Added node server + www with chart and table avg load times --- www/public/index.html | 181 ++++++++++++++++++++++++++++++++++++++++++ www/server.js | 96 ++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 www/public/index.html create mode 100644 www/server.js diff --git a/www/public/index.html b/www/public/index.html new file mode 100644 index 0000000..7faea6b --- /dev/null +++ b/www/public/index.html @@ -0,0 +1,181 @@ + + + + Chart + Tables - Comparing Total Load Times + + + + + + + +
+ + + + + + + + +
Ratio(%)
+
+ + + + + + + + +
Folder NameTotal Load Time [sec]
+ + + + diff --git a/www/server.js b/www/server.js new file mode 100644 index 0000000..9ef6568 --- /dev/null +++ b/www/server.js @@ -0,0 +1,96 @@ +import fs from 'fs/promises'; +import path from 'path'; +import express from 'express'; + +const app = express(); +const port = 3000; +const sourceFolder = 'output/time'; + +app.use(express.static('www/public')); + +async function readAndParseFile(filePath, folderName) { + const fileContent = await fs.readFile(filePath, 'utf-8'); + const logs = fileContent + .split('\n') + .filter((line) => line.startsWith('LOG=')) + .map((line) => { + const log = JSON.parse(line.slice(4)); + log.folderName = folderName; + return log; + }) + .filter((log) => log.loadTime > 0); + return logs; +} + +app.get('/data', async (req, res) => { + try { + const folders = await fs.readdir(sourceFolder + '/'); + const dataPromises = folders.map(async (folder) => { + const subfolders = await fs.readdir(path.join(sourceFolder, folder)); + return Promise.all( + subfolders.map(async (subfolder) => { + const subfolderPath = path.join(sourceFolder, folder, subfolder); + const stats = await fs.stat(subfolderPath); + if (stats.isDirectory()) { + const files = await fs.readdir(subfolderPath); + return Promise.all( + files.map(async (file) => { + if (file === '.gitkeep') { + return; + } + const filePath = path.join(subfolderPath, file); + const fileStats = await fs.stat(filePath); + if (fileStats.isFile()) { + const logs = await readAndParseFile( + filePath, + subfolderPath.replace(sourceFolder, ''), + ); + return logs.length > 0 ? logs : null; + } + }), + ); + } + }), + ); + }); + + const data = (await Promise.all(dataPromises)).flat(2).filter(Boolean); + const flattenedData = data.flat(2); + + const groupedByURLAndFolder = flattenedData.reduce((acc, log) => { + const key = `${log.url}-${log.folderName}`; + if (!acc[key]) { + acc[key] = { url: log.url, folderName: log.folderName, loadTimes: [] }; + } + acc[key].loadTimes.push(log.loadTime); + return acc; + }, {}); + + const result = Object.values(groupedByURLAndFolder).map( + ({ url, folderName, loadTimes }) => { + const averageLoadTime = + loadTimes.reduce((sum, loadTime) => sum + loadTime, 0) / + loadTimes.length; + return { url, folderName, loadTimes, averageLoadTime }; + }, + ); + + const folderSums = result.reduce((acc, { folderName, averageLoadTime }) => { + if (!acc[folderName]) { + acc[folderName] = { folderName, totalLoadTime: 0 }; + } + acc[folderName].totalLoadTime += averageLoadTime; + return acc; + }, {}); + + res.json({ result, folderSums }); + // console.log({ result, folderSums }); + } catch (err) { + console.error('Error:', err); + res.status(500).send('Error'); + } +}); + +app.listen(port, () => { + console.log(`Server running at http://localhost:${port}/`); +}); From 2718a94aef8f8efbec9063a0e5b636229c4b517a Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 10 Apr 2024 16:42:19 +0200 Subject: [PATCH 09/14] Update of readme --- README.md | 44 ++++++++++++++++++++++++++---- compareLoadTimeGraphTwoTables.png | Bin 0 -> 76949 bytes 2 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 compareLoadTimeGraphTwoTables.png diff --git a/README.md b/README.md index a3dfcf2..464b3a3 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Usage: ``` Example output: + ``` LOG: Addon temp path: LOG: Downloading addon @@ -43,19 +44,52 @@ LOG={"index":360,"url":"https://www.virgilio.it/","loadTime":1433,"loadedAt":"20 ``` +## How to show the results? + +The results are shown in the form of a graph and two tables. + +The graph shows the ratio of average pages opening time for the scenarios indicated. +The first table contains the results of the web pages load time measurements for different browser configurations and installed extensions. +The second table shows the total time taken to load web pages for different browser configurations and installed extensions, expressed in seconds. + +These scenarios are: + +1. How much faster is Chrome without Ghostery than Firefox without Ghostery? +2. How much faster is Chrome with Ghostery than Firefox with Ghostery? +3. How much faster is Firefox with Ghostery than without Ghostery? +4. How much faster is Chrome with Ghostery than without Ghostery? +5. How much faster is Firefox with Ghostery than with uBlock Origin? +6. How much faster is Chrome with Ghostery than with uBlock Origin? + +Firstly, download the output/timing measurements. Secondly, a simple server needs to be started. The script will load these measurements and calculate the average page opening time and calculate the total page loading time. To run the server: + +``` +node www/server.js +``` + +In any browser, open the following address: +http://localhost:3000/index.html + +Example output: + +``` + +![Compare load times](compareLoadTimeGraphTwoTables.png) + +``` + ## Convert the current to power based on the measurements taken from the benchmark To measure AC current, Gravity Analog AC Current Sensor was used. All the code to gather current value is available on https://wiki.dfrobot.com/Gravity_Analog_AC_Current_Sensor__SKU_SEN0211_ - Start calculate ``` npm run -- currentToPower ``` - Example input: + ``` 10:11:50.121 0.091 10:11:50.121 0.112 @@ -66,8 +100,8 @@ Example input: 10:11:50.152 0.095 ``` - Example output: + ``` [ { @@ -100,9 +134,6 @@ Example output: ] ``` - - - ## Based on the collected data from the benchmark, calculate the average time to load URLs Start read time @@ -119,6 +150,7 @@ Usage: ``` Example output: + ``` With Ghostery Average urls load time: diff --git a/compareLoadTimeGraphTwoTables.png b/compareLoadTimeGraphTwoTables.png new file mode 100644 index 0000000000000000000000000000000000000000..a2212461dc8272cf18e088e114b17c8aa00e3e07 GIT binary patch literal 76949 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%0DjBiVjkDA<5DIZWuf3z$WVMaeM!FaBUj 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 literal 0 HcmV?d00001 From 41b7070a657525b788812f445ff8289f40dc7025 Mon Sep 17 00:00:00 2001 From: GRadziejewski <40433708+GRadziejewski@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:49:32 +0200 Subject: [PATCH 10/14] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 464b3a3..f45ba05 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,8 @@ http://localhost:3000/index.html Example output: -``` - ![Compare load times](compareLoadTimeGraphTwoTables.png) -``` ## Convert the current to power based on the measurements taken from the benchmark From bb0bb697af418da64f42d1fda163e04fe5f20de5 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 10 Apr 2024 17:28:39 +0200 Subject: [PATCH 11/14] Moved all calculations to index.html file --- www/public/index.html | 112 ++++++++++++++++++++++++++---------------- www/server.js | 16 +----- 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/www/public/index.html b/www/public/index.html index 7faea6b..cf48724 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -49,85 +49,113 @@ function createTableRow(cells) { const row = document.createElement('tr'); - cells.forEach(cell => row.appendChild(cell)); + cells.forEach((cell) => row.appendChild(cell)); return row; } function calculateRatio( numeratorFolder, denominatorFolder, - folderSumsByName) { - return folderSumsByName[numeratorFolder] - .totalLoadTime / folderSumsByName[denominatorFolder] - .totalLoadTime; + folderSumsByName, + ) { + return ( + folderSumsByName[numeratorFolder].totalLoadTime / + folderSumsByName[denominatorFolder].totalLoadTime + ); + } + + function calculateFolderSums(data) { + const folderSums = data.reduce((acc, { folderName, loadTimes }) => { + if (!acc[folderName]) { + acc[folderName] = { folderName, totalLoadTime: 0, count: 0 }; + } + acc[folderName].totalLoadTime += loadTimes.reduce((a, b) => a + b, 0); + acc[folderName].count += loadTimes.length; + return acc; + }, {}); + + for (let key in folderSums) { + folderSums[key].averageLoadTime = + folderSums[key].totalLoadTime / folderSums[key].count; + } + + return folderSums; } fetch('/data') .then((response) => response.json()) .then((data) => { - const folderSums = Object.values(data.folderSums); - const loadTimeTableBody = document - .querySelector('#loadTimeTable tbody'); + const folderSums = calculateFolderSums(data); + const folderSumsArray = Object.values(folderSums); + const loadTimeTableBody = document.querySelector( + '#loadTimeTable tbody', + ); const folderNames = [ '/withGhostery/Firefox', '/withGhostery/Chrome', '/withUBlockOrigin/Firefox', '/withUBlockOrigin/Chrome', '/withoutExtensions/Firefox', - '/withoutExtensions/Chrome' + '/withoutExtensions/Chrome', ]; - const folderSumsByName = folderNames.reduce( - (acc, folderName) => { - acc[folderName] = folderSums.find((item) => - item.folderName === folderName); - return acc; + const folderSumsByName = folderNames.reduce((acc, folderName) => { + acc[folderName] = folderSumsArray.find( + (item) => item.folderName === folderName, + ); + return acc; }, {}); - folderSums.forEach((folderSum) => { + folderSumsArray.forEach((folderSum) => { const row = createTableRow([ createTableCell(folderSum.folderName), - createTableCell(folderSum.totalLoadTime.toFixed(2)) + createTableCell(folderSum.totalLoadTime.toFixed(2)), ]); loadTimeTableBody.appendChild(row); }); const ratios = { '1. How much faster is Chrome without Ghostery than Firefox without Ghostery?': - calculateRatio( - '/withoutExtensions/Firefox', - '/withoutExtensions/Chrome', - folderSumsByName), + calculateRatio( + '/withoutExtensions/Firefox', + '/withoutExtensions/Chrome', + folderSumsByName, + ), '2. How much faster is Chrome with Ghostery than Firefox with Ghostery?': - calculateRatio( - '/withGhostery/Firefox', - '/withGhostery/Chrome', - folderSumsByName), + calculateRatio( + '/withGhostery/Firefox', + '/withGhostery/Chrome', + folderSumsByName, + ), '3. How much faster is Firefox with Ghostery than without Ghostery?': - calculateRatio( - '/withoutExtensions/Firefox', - '/withGhostery/Firefox', - folderSumsByName), + calculateRatio( + '/withoutExtensions/Firefox', + '/withGhostery/Firefox', + folderSumsByName, + ), '4. How much faster is Chrome with Ghostery than without Ghostery?': - calculateRatio( - '/withoutExtensions/Chrome', - '/withGhostery/Chrome', - folderSumsByName), + calculateRatio( + '/withoutExtensions/Chrome', + '/withGhostery/Chrome', + folderSumsByName, + ), '5. How much faster is Firefox with Ghostery than with uBlock Origin?': - calculateRatio( - '/withUBlockOrigin/Firefox', - '/withGhostery/Firefox', - folderSumsByName), + calculateRatio( + '/withUBlockOrigin/Firefox', + '/withGhostery/Firefox', + folderSumsByName, + ), '6. How much faster is Chrome with Ghostery than with uBlock Origin?': - calculateRatio( - '/withUBlockOrigin/Chrome', - '/withGhostery/Chrome', - folderSumsByName), + calculateRatio( + '/withUBlockOrigin/Chrome', + '/withGhostery/Chrome', + folderSumsByName, + ), }; const ratioTableBody = document.querySelector('#ratioTable tbody'); @@ -145,9 +173,7 @@ const labels = Object.keys(ratios); const loadTimes = Object.values(ratios).map((ratio) => ratio * 100); - const ctx = document - .getElementById('compareTimes') - .getContext('2d'); + const ctx = document.getElementById('compareTimes').getContext('2d'); new Chart(ctx, { type: 'bar', data: { diff --git a/www/server.js b/www/server.js index 9ef6568..386e774 100644 --- a/www/server.js +++ b/www/server.js @@ -68,23 +68,11 @@ app.get('/data', async (req, res) => { const result = Object.values(groupedByURLAndFolder).map( ({ url, folderName, loadTimes }) => { - const averageLoadTime = - loadTimes.reduce((sum, loadTime) => sum + loadTime, 0) / - loadTimes.length; - return { url, folderName, loadTimes, averageLoadTime }; + return { url, folderName, loadTimes }; }, ); - const folderSums = result.reduce((acc, { folderName, averageLoadTime }) => { - if (!acc[folderName]) { - acc[folderName] = { folderName, totalLoadTime: 0 }; - } - acc[folderName].totalLoadTime += averageLoadTime; - return acc; - }, {}); - - res.json({ result, folderSums }); - // console.log({ result, folderSums }); + res.json(result); } catch (err) { console.error('Error:', err); res.status(500).send('Error'); From 00e86981d9c6c5cff6bc04e31072acbb689f05a5 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 10 Apr 2024 17:58:03 +0200 Subject: [PATCH 12/14] Changes in create/delete directiores logic --- index.js | 23 +++------------- output/current/idle/.gitkeep | 0 output/current/idleWithBrowser/.gitkeep | 0 output/current/withGhostery/.gitkeep | 0 output/current/withoutGhostery/.gitkeep | 0 output/time/withGhostery/.gitkeep | 0 output/time/withoutExtensions/.gitkeep | 0 output/timeCalculated/withGhostery/.gitkeep | 0 .../withGhostery/Chrome/.gitkeep | 0 .../withGhostery/Firefox/.gitkeep | 0 .../timeCalculated/withoutGhostery/.gitkeep | 0 .../withoutGhostery/Chrome/.gitkeep | 0 .../withoutGhostery/Firefox/.gitkeep | 0 profiles/withUBlockOrigin/.gitkeep | 0 profiles/withoutExtensions/.gitkeep | 0 src/directories.js | 27 +++++++++++++++++++ 16 files changed, 30 insertions(+), 20 deletions(-) delete mode 100644 output/current/idle/.gitkeep delete mode 100644 output/current/idleWithBrowser/.gitkeep delete mode 100644 output/current/withGhostery/.gitkeep delete mode 100644 output/current/withoutGhostery/.gitkeep delete mode 100644 output/time/withGhostery/.gitkeep delete mode 100644 output/time/withoutExtensions/.gitkeep delete mode 100644 output/timeCalculated/withGhostery/.gitkeep delete mode 100644 output/timeCalculated/withGhostery/Chrome/.gitkeep delete mode 100644 output/timeCalculated/withGhostery/Firefox/.gitkeep delete mode 100644 output/timeCalculated/withoutGhostery/.gitkeep delete mode 100644 output/timeCalculated/withoutGhostery/Chrome/.gitkeep delete mode 100644 output/timeCalculated/withoutGhostery/Firefox/.gitkeep delete mode 100644 profiles/withUBlockOrigin/.gitkeep delete mode 100644 profiles/withoutExtensions/.gitkeep create mode 100644 src/directories.js diff --git a/index.js b/index.js index 2f268cb..6917c29 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import { createFolders, deleteFolders, } from './src/helpers.js'; +import { directoriesOutput, directoriesProfiles } from './src/directories.js'; const timestamp = new Date().toISOString(); const isRegionEU = Boolean(process.argv.find((arg) => arg === '--EU')); @@ -28,32 +29,14 @@ let selectedExtension = { isUBlockOrigin: Boolean(process.argv.find((arg) => arg === '--with-uBO')), }; -const directoriesOutput = [ - './output/time/withGhostery/Firefox', - './output/time/withGhostery/Chrome', - './output/time/withUBlockOrigin/Firefox', - './output/time/withUBlockOrigin/Chrome', - './output/time/withoutExtensions/Firefox', - './output/time/withoutExtensions/Chrome', -]; - -const directoriesProfiles = [ - './profiles/withGhostery/Firefox', - './profiles/withGhostery/Chrome', - './profiles/withUBlockOrigin/Firefox', - './profiles/withUBlockOrigin/Chrome', - './profiles/withoutExtensions/Firefox', - './profiles/withoutExtensions/Chrome', -]; - -createFolders(directoriesOutput); -createFolders(directoriesProfiles); if (deleteProfilesFolders) { deleteFolders(directoriesProfiles); } if (deleteOutputsFolders) { deleteFolders(directoriesOutput); } +createFolders(directoriesOutput); +createFolders(directoriesProfiles); const extensionUrls = { Firefox: { diff --git a/output/current/idle/.gitkeep b/output/current/idle/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/current/idleWithBrowser/.gitkeep b/output/current/idleWithBrowser/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/current/withGhostery/.gitkeep b/output/current/withGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/current/withoutGhostery/.gitkeep b/output/current/withoutGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/time/withGhostery/.gitkeep b/output/time/withGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/time/withoutExtensions/.gitkeep b/output/time/withoutExtensions/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withGhostery/.gitkeep b/output/timeCalculated/withGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withGhostery/Chrome/.gitkeep b/output/timeCalculated/withGhostery/Chrome/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withGhostery/Firefox/.gitkeep b/output/timeCalculated/withGhostery/Firefox/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withoutGhostery/.gitkeep b/output/timeCalculated/withoutGhostery/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withoutGhostery/Chrome/.gitkeep b/output/timeCalculated/withoutGhostery/Chrome/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/output/timeCalculated/withoutGhostery/Firefox/.gitkeep b/output/timeCalculated/withoutGhostery/Firefox/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/profiles/withUBlockOrigin/.gitkeep b/profiles/withUBlockOrigin/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/profiles/withoutExtensions/.gitkeep b/profiles/withoutExtensions/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/directories.js b/src/directories.js new file mode 100644 index 0000000..d8361c3 --- /dev/null +++ b/src/directories.js @@ -0,0 +1,27 @@ +export const directoriesOutput = [ + './output/current/idle', + './output/current/idleWithBrowser', + './output/current/withGhostery', + './output/current/withoutGhostery', + './output/time/withGhostery/Firefox', + './output/time/withGhostery/Chrome', + './output/time/withUBlockOrigin/Firefox', + './output/time/withUBlockOrigin/Chrome', + './output/time/withoutExtensions/Firefox', + './output/time/withoutExtensions/Chrome', + './output/timeCalculated/withGhostery/Firefox', + './output/timeCalculated/withGhostery/Chrome', + './output/timeCalculated/withoutExtensions/Firefox', + './output/timeCalculated/withoutExtensions/Chrome', + './output/timeCalculated/withoutGhostery/Firefox', + './output/timeCalculated/withoutGhostery/Chrome', +]; + +export const directoriesProfiles = [ + './profiles/withGhostery/Firefox', + './profiles/withGhostery/Chrome', + './profiles/withUBlockOrigin/Firefox', + './profiles/withUBlockOrigin/Chrome', + './profiles/withoutExtensions/Firefox', + './profiles/withoutExtensions/Chrome', +]; From b0d9e247e88c91b7de3a906e67739d6a60a32cf7 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Wed, 10 Apr 2024 18:08:47 +0200 Subject: [PATCH 13/14] Added directory creation/deletion, no need for .gitkeep --- .gitignore | 2 -- www/server.js | 3 --- 2 files changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index 1012cbb..444612d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ profiles/withGhostery/** profiles/withoutExtensions/** profiles/withUBlockOrigin/** -# Do not ignore a special file name -!.gitkeep diff --git a/www/server.js b/www/server.js index 386e774..912b6b9 100644 --- a/www/server.js +++ b/www/server.js @@ -35,9 +35,6 @@ app.get('/data', async (req, res) => { const files = await fs.readdir(subfolderPath); return Promise.all( files.map(async (file) => { - if (file === '.gitkeep') { - return; - } const filePath = path.join(subfolderPath, file); const fileStats = await fs.stat(filePath); if (fileStats.isFile()) { From 4065be5d035f1ed629a615bab85c0a9813691308 Mon Sep 17 00:00:00 2001 From: Grzegorz Radziejewski Date: Thu, 11 Apr 2024 16:49:43 +0200 Subject: [PATCH 14/14] Small changes --- README.md | 5 ++--- www/public/index.html | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f45ba05..bafb3a0 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ Usage: --chrome select Chrome browser --with-ghostery load Ghostery extension --with-uBO load uBlock Origin extension ---delete-profiles-folders delete browser profile folders ---delete-output-folders delete output folders +--delete-profiles-folders delete browser profile folders to make a clear run +--delete-output-folders delete output folders to make a clear run ``` Example output: @@ -74,7 +74,6 @@ Example output: ![Compare load times](compareLoadTimeGraphTwoTables.png) - ## Convert the current to power based on the measurements taken from the benchmark To measure AC current, Gravity Analog AC Current Sensor was used. All the code to gather current value is available on https://wiki.dfrobot.com/Gravity_Analog_AC_Current_Sensor__SKU_SEN0211_ diff --git a/www/public/index.html b/www/public/index.html index cf48724..aa7b7dd 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -41,6 +41,15 @@