diff --git a/development/build/scripts.js b/development/build/scripts.js index 798edd9db..7cdba547a 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -773,7 +773,7 @@ function setupBundlerDefaults( // Look for TypeScript files when walking the dependency tree extensions, // Use entryFilepath for moduleIds, easier to determine origin file - fullPaths: devMode, + fullPaths: devMode || testing, // For sourcemaps debug: true, }); diff --git a/package.json b/package.json index e68dec500..dc9b0259f 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,13 @@ "setup:postinstall": "yarn patch-package && yarn allow-scripts", "start": "yarn build:dev dev --apply-lavamoat=false", "start:lavamoat": "yarn build:dev dev --apply-lavamoat=true", - "start:mv3": "ENABLE_MV3=true yarn build:dev dev --apply-lavamoat=false", + "start:mv3": "ENABLE_MV3=true yarn build:dev dev --apply-lavamoat=true", "dist": "yarn build prod", "build": "yarn lavamoat:build", "build:dev": "node development/build/index.js", "start:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' yarn build testDev", "benchmark:chrome": "SELENIUM_BROWSER=chrome node test/e2e/benchmark.js", + "mv3:stats:chrome": "SELENIUM_BROWSER=chrome ENABLE_MV3=true node test/e2e/lavamoat-stats.js", "benchmark:firefox": "SELENIUM_BROWSER=firefox node test/e2e/benchmark.js", "build:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' yarn build test", "build:test:flask": "yarn build test --build-type flask", diff --git a/test/e2e/lavamoat-stats.js b/test/e2e/lavamoat-stats.js new file mode 100644 index 000000000..4abf00a68 --- /dev/null +++ b/test/e2e/lavamoat-stats.js @@ -0,0 +1,140 @@ +#!/usr/bin/env node + +/* eslint-disable node/shebang */ +const path = require('path'); +const { promises: fs, constants: fsConstants } = require('fs'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); +const { exitWithError } = require('../../development/lib/exit-with-error'); +const { withFixtures, tinyDelayMs } = require('./helpers'); + +async function measurePage() { + let metrics; + try { + await withFixtures({ fixtures: 'imported-account' }, async ({ driver }) => { + await driver.delay(tinyDelayMs); + await driver.navigate(); + await driver.findElement('#password'); + await driver.delay(1000); + const logs = await driver.checkBrowserForLavamoatLogs(); + + let logString = ''; + let inObject = false; + + const parsedLogs = []; + + logs.forEach((log) => { + if (log.indexOf('"version": 1') >= 0) { + logString += log; + parsedLogs.push(`{${logString}}`); + logString = ''; + inObject = false; + } else if (inObject) { + logString += log; + } else if ( + log.search(/"name": ".*app\/scripts\/background.js",/u) >= 0 || + log.search(/"name": ".*app\/scripts\/ui.js",/u) >= 0 + ) { + logString += log; + inObject = true; + } + }); + + metrics = parsedLogs.map((pl) => JSON.parse(pl)); + }); + } catch (error) { + // do nothing + } + return metrics; +} + +async function profilePageLoad() { + const results = await measurePage(); + const metrics = {}; + + metrics['background.js'] = results[0]; + metrics['ui.js'] = results[1]; + + return metrics; +} + +async function isWritable(directory) { + try { + await fs.access(directory, fsConstants.W_OK); + return true; + } catch (error) { + if (error.code !== 'EACCES') { + throw error; + } + return false; + } +} + +async function getFirstParentDirectoryThatExists(directory) { + let nextDirectory = directory; + for (;;) { + try { + await fs.access(nextDirectory, fsConstants.F_OK); + return nextDirectory; + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } else if (nextDirectory === path.dirname(nextDirectory)) { + throw new Error('Failed to find parent directory that exists'); + } + nextDirectory = path.dirname(nextDirectory); + } + } +} + +async function main() { + const { argv } = yargs(hideBin(process.argv)).usage( + '$0 [options]', + 'Run a page load benchmark', + (_yargs) => + _yargs.option('out', { + description: + 'Output filename. Output printed to STDOUT of this is omitted.', + type: 'string', + normalize: true, + }), + ); + + const { out } = argv; + + let outputDirectory; + let existingParentDirectory; + if (out) { + outputDirectory = path.dirname(out); + existingParentDirectory = await getFirstParentDirectoryThatExists( + outputDirectory, + ); + if (!(await isWritable(existingParentDirectory))) { + throw new Error('Specified output file directory is not writable'); + } + } + + const results = await profilePageLoad(); + + if (out) { + if (outputDirectory !== existingParentDirectory) { + await fs.mkdir(outputDirectory, { recursive: true }); + } + + await fs.writeFile( + path.join(out, 'background.json'), + JSON.stringify(results['background.js'], null, 2), + ); + + await fs.writeFile( + path.join(out, 'ui.json'), + JSON.stringify(results['ui.js'], null, 2), + ); + } else { + console.log(JSON.stringify(results, null, 2)); + } +} + +main().catch((error) => { + exitWithError(error); +}); diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js index a9df3e646..09069906c 100644 --- a/test/e2e/webdriver/chrome.js +++ b/test/e2e/webdriver/chrome.js @@ -18,7 +18,14 @@ class ChromeDriver { if (responsive) { args.push('--auto-open-devtools-for-tabs'); } - args.push('--log-level=3'); + + if (process.env.ENABLE_MV3) { + args.push('--log-level=0'); + args.push('--enable-logging'); + args.push(`--user-data-dir=${process.cwd()}/test-artifacts/chrome`); + } else { + args.push('--log-level=3'); + } const options = new chrome.Options().addArguments(args); options.setProxy(proxy.manual({ https: HTTPS_PROXY_HOST })); options.setAcceptInsecureCerts(true); diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 73c029bfe..9886872a4 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -397,6 +397,20 @@ class Driver { ); } + async checkBrowserForLavamoatLogs() { + const browserLogs = ( + await fs.readFile( + `${process.cwd()}/test-artifacts/chrome/chrome_debug.log`, + ) + ) + .toString('utf-8') + .split(/\r?\n/u); + + await fs.writeFile('/tmp/all_logs.json', JSON.stringify(browserLogs)); + + return browserLogs; + } + async checkBrowserForConsoleErrors() { const ignoredLogTypes = ['WARNING']; const ignoredErrorMessages = [