const fs = require('fs') const os = require('os') const path = require('path') const { Builder, By, until } = require('selenium-webdriver') const firefox = require('selenium-webdriver/firefox') const { Command } = require('selenium-webdriver/lib/command') /** * The prefix for temporary Firefox profiles. All Firefox profiles used for e2e tests * will be created as random directories inside this. * @type {string} */ const TEMP_PROFILE_PATH_PREFIX = path.join(os.tmpdir(), 'MetaMask-Fx-Profile') const GeckoDriverCommand = { INSTALL_ADDON: 'install addon', } /** * A wrapper around a {@code WebDriver} instance exposing Firefox-specific functionality */ class FirefoxDriver { /** * Builds a {@link FirefoxDriver} instance * @param {{extensionPath: string}} options - the options for the build * @returns {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>} */ static async build({ extensionPath, responsive, port }) { const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX) const options = new firefox.Options().setProfile(templateProfile) const builder = new Builder() .forBrowser('firefox') .setFirefoxOptions(options) if (port) { const service = new firefox.ServiceBuilder().setPort(port) builder.setFirefoxService(service) } const driver = builder.build() const fxDriver = new FirefoxDriver(driver) await fxDriver.init() const extensionId = await fxDriver.installExtension(extensionPath) const internalExtensionId = await fxDriver.getInternalId() if (responsive) { await driver.manage().window().setRect({ width: 320, height: 600 }) } return { driver, extensionId, extensionUrl: `moz-extension://${internalExtensionId}`, } } /** * @constructor * @param {!ThenableWebDriver} driver - a {@code WebDriver} instance */ constructor(driver) { this._driver = driver } /** * Initializes the driver * @returns {Promise} */ async init() { await this._driver .getExecutor() .defineCommand( GeckoDriverCommand.INSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/install', ) } /** * Installs the extension at the given path * @param {string} addonPath - the path to the unpacked extension or XPI * @returns {Promise} the extension ID */ async installExtension(addonPath) { const cmd = new Command(GeckoDriverCommand.INSTALL_ADDON) .setParameter('path', path.resolve(addonPath)) .setParameter('temporary', true) return await this._driver.execute(cmd) } /** * Returns the Internal UUID for the given extension * @returns {Promise} the Internal UUID for the given extension */ async getInternalId() { await this._driver.get('about:debugging#addons') return await this._driver .wait( until.elementLocated( By.xpath("//dl/div[contains(., 'Internal UUID')]/dd"), ), 1000, ) .getText() } } module.exports = FirefoxDriver