mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Refactor webdriver setup (#7690)
The setup for each browser web driver instance has been extracted from `helpers.js` and moved to the `webdriver` directory.
This commit is contained in:
parent
eadeaa7883
commit
12fb5ad985
@ -1,12 +1,10 @@
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
|
||||||
const mkdirp = require('mkdirp')
|
const mkdirp = require('mkdirp')
|
||||||
const pify = require('pify')
|
const pify = require('pify')
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const os = require('os')
|
const { until } = require('selenium-webdriver')
|
||||||
const { By, Builder, until } = require('selenium-webdriver')
|
|
||||||
const { Command } = require('selenium-webdriver/lib/command')
|
|
||||||
|
|
||||||
|
const { buildWebDriver } = require('./webdriver')
|
||||||
const fetchMockResponses = require('./fetch-mocks.json')
|
const fetchMockResponses = require('./fetch-mocks.json')
|
||||||
|
|
||||||
const tinyDelayMs = 200
|
const tinyDelayMs = 200
|
||||||
@ -33,30 +31,10 @@ module.exports = {
|
|||||||
|
|
||||||
|
|
||||||
async function prepareExtensionForTesting ({ responsive } = {}) {
|
async function prepareExtensionForTesting ({ responsive } = {}) {
|
||||||
let driver, extensionId, extensionUrl
|
const browser = process.env.SELENIUM_BROWSER
|
||||||
const targetBrowser = process.env.SELENIUM_BROWSER
|
const extensionPath = `dist/${browser}`
|
||||||
switch (targetBrowser) {
|
const { driver, extensionId, extensionUrl } = await buildWebDriver({ browser, extensionPath, responsive })
|
||||||
case 'chrome': {
|
|
||||||
const extPath = path.resolve('dist/chrome')
|
|
||||||
driver = buildChromeWebDriver(extPath, { responsive })
|
|
||||||
await delay(largeDelayMs)
|
|
||||||
extensionId = await getExtensionIdChrome(driver)
|
|
||||||
extensionUrl = `chrome-extension://${extensionId}/home.html`
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'firefox': {
|
|
||||||
const extPath = path.resolve('dist/firefox')
|
|
||||||
driver = buildFirefoxWebdriver({ responsive })
|
|
||||||
await installWebExt(driver, extPath)
|
|
||||||
await delay(largeDelayMs)
|
|
||||||
extensionId = await getExtensionIdFirefox(driver)
|
|
||||||
extensionUrl = `moz-extension://${extensionId}/home.html`
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new Error(`prepareExtensionForTesting - unable to prepare extension for unknown browser "${targetBrowser}"`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Depending on the state of the application built into the above directory (extPath) and the value of
|
// Depending on the state of the application built into the above directory (extPath) and the value of
|
||||||
// METAMASK_DEBUG we will see different post-install behaviour and possibly some extra windows. Here we
|
// METAMASK_DEBUG we will see different post-install behaviour and possibly some extra windows. Here we
|
||||||
// are closing any extraneous windows to reset us to a single window before continuing.
|
// are closing any extraneous windows to reset us to a single window before continuing.
|
||||||
@ -104,56 +82,6 @@ async function setupFetchMocking (driver) {
|
|||||||
await driver.executeScript(`(${fetchMocking})(${fetchMockResponsesJson})`)
|
await driver.executeScript(`(${fetchMocking})(${fetchMockResponsesJson})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildChromeWebDriver (extPath, opts = {}) {
|
|
||||||
const tmpProfile = fs.mkdtempSync(path.join(os.tmpdir(), 'mm-chrome-profile'))
|
|
||||||
const args = [
|
|
||||||
`load-extension=${extPath}`,
|
|
||||||
`user-data-dir=${tmpProfile}`,
|
|
||||||
]
|
|
||||||
if (opts.responsive) {
|
|
||||||
args.push('--auto-open-devtools-for-tabs')
|
|
||||||
}
|
|
||||||
return new Builder()
|
|
||||||
.withCapabilities({
|
|
||||||
chromeOptions: {
|
|
||||||
args,
|
|
||||||
binary: process.env.SELENIUM_CHROME_BINARY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildFirefoxWebdriver (opts = {}) {
|
|
||||||
const driver = new Builder().build()
|
|
||||||
if (opts.responsive) {
|
|
||||||
driver.manage().window().setSize(320, 600)
|
|
||||||
}
|
|
||||||
return driver
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExtensionIdChrome (driver) {
|
|
||||||
await driver.get('chrome://extensions')
|
|
||||||
const extensionId = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-item-list").shadowRoot.querySelector("extensions-item:nth-child(2)").getAttribute("id")')
|
|
||||||
return extensionId
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExtensionIdFirefox (driver) {
|
|
||||||
await driver.get('about:debugging#addons')
|
|
||||||
const extensionId = await driver.wait(until.elementLocated(By.xpath('//dl/div[contains(., \'Internal UUID\')]/dd')), 1000).getText()
|
|
||||||
return extensionId
|
|
||||||
}
|
|
||||||
|
|
||||||
async function installWebExt (driver, extension) {
|
|
||||||
const cmd = await new Command('moz-install-web-ext')
|
|
||||||
.setParameter('path', path.resolve(extension))
|
|
||||||
.setParameter('temporary', true)
|
|
||||||
|
|
||||||
await driver.getExecutor()
|
|
||||||
.defineCommand(cmd.getName(), 'POST', '/session/:sessionId/moz/addon/install')
|
|
||||||
|
|
||||||
return await driver.schedule(cmd, 'installWebExt(' + extension + ')')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkBrowserForConsoleErrors (driver) {
|
async function checkBrowserForConsoleErrors (driver) {
|
||||||
const ignoredLogTypes = ['WARNING']
|
const ignoredLogTypes = ['WARNING']
|
||||||
const ignoredErrorMessages = [
|
const ignoredErrorMessages = [
|
||||||
|
63
test/e2e/webdriver/chrome.js
Normal file
63
test/e2e/webdriver/chrome.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
const { Builder } = require('selenium-webdriver')
|
||||||
|
const chrome = require('selenium-webdriver/chrome')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around a {@code WebDriver} instance exposing Chrome-specific functionality
|
||||||
|
*/
|
||||||
|
class ChromeDriver {
|
||||||
|
static async build ({ extensionPath, responsive }) {
|
||||||
|
const args = [
|
||||||
|
`load-extension=${extensionPath}`,
|
||||||
|
]
|
||||||
|
if (responsive) {
|
||||||
|
args.push('--auto-open-devtools-for-tabs')
|
||||||
|
}
|
||||||
|
const options = new chrome.Options()
|
||||||
|
.addArguments(args)
|
||||||
|
const driver = new Builder()
|
||||||
|
.forBrowser('chrome')
|
||||||
|
.setChromeOptions(options)
|
||||||
|
.build()
|
||||||
|
const chromeDriver = new ChromeDriver(driver)
|
||||||
|
const extensionId = await chromeDriver.getExtensionIdByName('MetaMask')
|
||||||
|
|
||||||
|
return {
|
||||||
|
driver,
|
||||||
|
extensionUrl: `chrome-extension://${extensionId}/home.html`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {!ThenableWebDriver} driver a {@code WebDriver} instance
|
||||||
|
*/
|
||||||
|
constructor (driver) {
|
||||||
|
this._driver = driver
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extension ID for the given extension name
|
||||||
|
* @param {string} extensionName the extension name
|
||||||
|
* @return {Promise<string|undefined>} the extension ID
|
||||||
|
*/
|
||||||
|
async getExtensionIdByName (extensionName) {
|
||||||
|
await this._driver.get('chrome://extensions')
|
||||||
|
return await this._driver.executeScript(`
|
||||||
|
const extensions = document.querySelector("extensions-manager").shadowRoot
|
||||||
|
.querySelector("extensions-item-list").shadowRoot
|
||||||
|
.querySelectorAll("extensions-item")
|
||||||
|
|
||||||
|
for (let i = 0; i < extensions.length; i++) {
|
||||||
|
const extension = extensions[i].shadowRoot
|
||||||
|
const name = extension.querySelector('#name').textContent
|
||||||
|
if (name === "${extensionName}") {
|
||||||
|
return extensions[i].getAttribute("id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ChromeDriver
|
99
test/e2e/webdriver/firefox.js
Normal file
99
test/e2e/webdriver/firefox.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
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
|
||||||
|
* @return {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>}
|
||||||
|
*/
|
||||||
|
static async build ({ extensionPath, responsive }) {
|
||||||
|
const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX)
|
||||||
|
const profile = new firefox.Profile(templateProfile)
|
||||||
|
const options = new firefox.Options()
|
||||||
|
.setProfile(profile)
|
||||||
|
const driver = new Builder()
|
||||||
|
.forBrowser('firefox')
|
||||||
|
.setFirefoxOptions(options)
|
||||||
|
.build()
|
||||||
|
const fxDriver = new FirefoxDriver(driver)
|
||||||
|
|
||||||
|
await fxDriver.init()
|
||||||
|
|
||||||
|
const extensionId = await fxDriver.installExtension(extensionPath)
|
||||||
|
const internalExtensionId = await fxDriver.getInternalId()
|
||||||
|
|
||||||
|
if (responsive) {
|
||||||
|
driver.manage().window().setSize(320, 600)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
driver,
|
||||||
|
extensionId,
|
||||||
|
extensionUrl: `moz-extension://${internalExtensionId}/home.html`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {!ThenableWebDriver} driver a {@code WebDriver} instance
|
||||||
|
*/
|
||||||
|
constructor (driver) {
|
||||||
|
this._driver = driver
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the driver
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* @return {Promise<string>} 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.schedule(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Internal UUID for the given extension
|
||||||
|
* @return {Promise<string>} 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
|
33
test/e2e/webdriver/index.js
Normal file
33
test/e2e/webdriver/index.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { Browser } = require('selenium-webdriver')
|
||||||
|
const ChromeDriver = require('./chrome')
|
||||||
|
const FirefoxDriver = require('./firefox')
|
||||||
|
|
||||||
|
const buildWebDriver = async function buildWebDriver ({ browser, extensionPath, responsive }) {
|
||||||
|
switch (browser) {
|
||||||
|
case Browser.CHROME: {
|
||||||
|
const { driver, extensionId, extensionUrl } = await ChromeDriver.build({ extensionPath, responsive })
|
||||||
|
|
||||||
|
return {
|
||||||
|
driver,
|
||||||
|
extensionId,
|
||||||
|
extensionUrl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Browser.FIREFOX: {
|
||||||
|
const { driver, extensionId, extensionUrl } = await FirefoxDriver.build({ extensionPath, responsive })
|
||||||
|
|
||||||
|
return {
|
||||||
|
driver,
|
||||||
|
extensionId,
|
||||||
|
extensionUrl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error(`Unrecognized browser: ${browser}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildWebDriver,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user