diff --git a/lib/chrome/webdriver/builder.js b/lib/chrome/webdriver/builder.js index 655440cfe0..36d4f4a699 100644 --- a/lib/chrome/webdriver/builder.js +++ b/lib/chrome/webdriver/builder.js @@ -9,7 +9,7 @@ import { pick, isEmpty, getProperty } from '../../support/util.js'; * @param builder * @param {Object} options the options for a web driver. */ -export async function configureBuilder(builder, baseDir, options) { +export async function configureBuilder(builder, baseDir, options, tmpDir) { const chromeConfig = options.chrome || {}; let chromedriverPath = getProperty(chromeConfig, 'chromedriverPath'); @@ -35,6 +35,14 @@ export async function configureBuilder(builder, baseDir, options) { if (options.verbose >= 3 || chromeConfig.enableVerboseChromeDriverLog) serviceBuilder.enableVerboseLogging(); } + if (tmpDir) { + serviceBuilder.setEnvironment({ + ...process.env, + TMPDIR: tmpDir, + TMP: tmpDir, + TEMP: tmpDir + }); + } builder.setChromeService(serviceBuilder); const proxyPacSettings = pick(options.proxy, ['pac']); diff --git a/lib/core/seleniumRunner.js b/lib/core/seleniumRunner.js index 0b6d00fe6b..b2bb106d50 100644 --- a/lib/core/seleniumRunner.js +++ b/lib/core/seleniumRunner.js @@ -1,3 +1,6 @@ +import { mkdtemp, rm } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; import { getLogger } from '@sitespeed.io/log'; import { Condition, error as webdriverError } from 'selenium-webdriver'; import { isAndroidConfigured } from '../android/index.js'; @@ -77,11 +80,18 @@ export class SeleniumRunner { * @throws {BrowserError} if the browser can't start */ async start() { + // Chromedriver deletes its temporary profile a few seconds after the + // browser closes, but Selenium stops Chromedriver before that. Chrome + // also leaves files in the temp folder. Give them their own folder and + // remove it in stop(). + if (this.options.browser === 'chrome' || this.options.browser === 'edge') { + this.tmpDir = await mkdtemp(path.join(tmpdir(), 'browsertime-')); + } const tries = this.browserRestartTries; for (let index = 0; index < tries; ++index) { try { this.driver = await timeout( - createWebDriver(this.baseDir, this.options), + createWebDriver(this.baseDir, this.options, this.tmpDir), this.options.timeouts.browserStart, `Failed to start ${this.options.browser} in ${ this.options.timeouts.browserStart / 1000 @@ -97,6 +107,7 @@ export class SeleniumRunner { } } if (!this.driver) { + await this.removeTmpDir(); throw new BrowserError( `Could not start ${this.options.browser} with ${tries} tries` ); @@ -527,7 +538,7 @@ export class SeleniumRunner { this.driver.quit(), 120_000, 'Could not close the browser using driver.quit' - ); + ).finally(() => this.removeTmpDir()); } catch (error) { throw new BrowserError(error.message, { cause: error @@ -536,6 +547,13 @@ export class SeleniumRunner { } } + async removeTmpDir() { + if (this.tmpDir) { + await rm(this.tmpDir, { recursive: true, force: true, maxRetries: 3 }); + this.tmpDir = undefined; + } + } + /** * * Scripts should be valid statements or IIFEs '(function() {...})()' that can run diff --git a/lib/core/webdriver/index.js b/lib/core/webdriver/index.js index a0c026df9f..13838f8423 100644 --- a/lib/core/webdriver/index.js +++ b/lib/core/webdriver/index.js @@ -11,7 +11,7 @@ import { isEmpty } from '../../support/util.js'; * @returns {!Promise} a promise that resolves to the webdriver, * or rejects if the current configuration is invalid. */ -export async function createWebDriver(baseDir, options) { +export async function createWebDriver(baseDir, options, tmpDir) { const browser = options.browser || 'chrome'; const seleniumUrl = options.selenium ? options.selenium.url : undefined; const capabilities = options.selenium @@ -31,7 +31,7 @@ export async function createWebDriver(baseDir, options) { switch (browser) { case 'chrome': { - await configureBuilderChrome(builder, baseDir, options); + await configureBuilderChrome(builder, baseDir, options, tmpDir); break; } @@ -46,7 +46,7 @@ export async function createWebDriver(baseDir, options) { } case 'edge': { - await configureBuilderEdge(builder, baseDir, options); + await configureBuilderEdge(builder, baseDir, options, tmpDir); break; } diff --git a/lib/edge/webdriver/builder.js b/lib/edge/webdriver/builder.js index e996039158..f0920a487c 100644 --- a/lib/edge/webdriver/builder.js +++ b/lib/edge/webdriver/builder.js @@ -6,7 +6,7 @@ import { pac, manual } from 'selenium-webdriver/proxy.js'; import { pick, isEmpty, getProperty } from '../../support/util.js'; const log = getLogger('browsertime.edge'); -export async function configureBuilder(builder, baseDir, options) { +export async function configureBuilder(builder, baseDir, options, tmpDir) { const edgeConfig = options.edge || {}; const chromeConfig = options.chrome || {}; @@ -37,6 +37,14 @@ export async function configureBuilder(builder, baseDir, options) { if (options.verbose >= 3 || chromeConfig.enableVerboseChromeDriverLog) serviceBuilder.enableVerboseLogging(); } + if (tmpDir) { + serviceBuilder.setEnvironment({ + ...process.env, + TMPDIR: tmpDir, + TMP: tmpDir, + TEMP: tmpDir + }); + } builder.setEdgeService(serviceBuilder); log.info(