Move webdriver interactions into driver module (#7798)

The Selenium webdriver is difficult to use, and easy to misuse. To help
use the driver and make it easier to maintain our e2e tests, all driver
interactions are now performed via a `driver` module. This is basically
a wrapper class around the `selenium-webdriver` that exposes only the
methods we want to use directly, along with all of our helper methods.
feature/default_network_editable
Mark Stacey 5 years ago committed by GitHub
parent d802e09a8b
commit 27cfb6aa51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 0
      test/data/fetch-mocks.json
  2. 160
      test/e2e/address-book.spec.js
  3. 107
      test/e2e/ethereum-on.spec.js
  4. 235
      test/e2e/from-import-ui.spec.js
  5. 156
      test/e2e/helpers.js
  6. 108
      test/e2e/incremental-security.spec.js
  7. 140
      test/e2e/metamask-responsive-ui.spec.js
  8. 1019
      test/e2e/metamask-ui.spec.js
  9. 114
      test/e2e/permissions.spec.js
  10. 141
      test/e2e/send-edit.spec.js
  11. 83
      test/e2e/signature-request.spec.js
  12. 149
      test/e2e/threebox.spec.js
  13. 94
      test/e2e/web3.spec.js
  14. 153
      test/e2e/webdriver/driver.js
  15. 44
      test/e2e/webdriver/index.js
  16. 2
      test/integration/lib/confirm-sig-requests.js
  17. 2
      test/integration/lib/currency-localization.js
  18. 2
      test/integration/lib/tx-list-items.js

@ -1,15 +1,11 @@
const assert = require('assert')
const webdriver = require('selenium-webdriver')
const { By, until } = require('selenium-webdriver')
const { By, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
verboseReportOnFailure,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -20,9 +16,6 @@ describe('MetaMask', function () {
let driver
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -38,12 +31,11 @@ describe('MetaMask', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors()
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -51,7 +43,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -62,63 +54,63 @@ describe('MetaMask', function () {
describe('Going through the first time flow', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
const button = await findElement(driver, By.css('.first-time-flow__form button'))
const passwordBox = await driver.findElement(By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await driver.findElement(By.css('.first-time-flow__form #confirm-password'))
const button = await driver.findElement(By.css('.first-time-flow__form button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
let seedPhrase
it('reveals the seed phrase', async () => {
const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
const revealSeedPhraseButton = await driver.findElement(byRevealButton, 10000)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText()
const revealedSeedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words'))
seedPhrase = await revealedSeedPhrase.getText()
assert.equal(seedPhrase.split(' ').length, 12)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
async function clickWordAndWait (word) {
const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]`
const word0 = await findElement(driver, By.xpath(xpath), 10000)
const word0 = await driver.findElement(By.xpath(xpath))
await word0.click()
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
}
it('can retype the seed phrase', async () => {
@ -128,146 +120,148 @@ describe('MetaMask', function () {
await clickWordAndWait(word)
}
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirm = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Import seed phrase', () => {
it('logs out of the vault', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenu = await driver.findElement(By.css('.account-menu__icon'))
await accountMenu.click()
await driver.delay(regularDelayMs)
const logoutButton = await findElement(driver, By.css('.account-menu__logout-button'))
const logoutButton = await driver.findElement(By.css('.account-menu__logout-button'))
assert.equal(await logoutButton.getText(), 'Log out')
await logoutButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('imports seed phrase', async () => {
const restoreSeedLink = await findElement(driver, By.css('.unlock-page__link--import'))
const restoreSeedLink = await driver.findElement(By.css('.unlock-page__link--import'))
assert.equal(await restoreSeedLink.getText(), 'Import using account seed phrase')
await restoreSeedLink.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const seedTextArea = await findElement(driver, By.css('textarea'))
const seedTextArea = await driver.findElement(By.css('textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const passwordInputs = await driver.findElements(By.css('input'))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await passwordInputs[0].sendKeys('correct horse battery staple')
await passwordInputs[1].sendKeys('correct horse battery staple')
await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.restore.message}')]`)).click()
await delay(regularDelayMs)
const restoreButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.restore.message}')]`))
restoreButton.click()
await driver.delay(regularDelayMs)
})
it('balance renders', async () => {
const balance = await findElement(driver, By.css('.balance-display .token-amount'))
const balance = await driver.findElement(By.css('.balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /25\s*ETH/))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Adds an entry to the address book and sends eth to that address', () => {
it('starts a send transaction', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
const sendButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const inputAddress = await findElement(driver, By.css('input[placeholder="Search, public address (0x), or ENS"]'))
const inputAddress = await driver.findElement(By.css('input[placeholder="Search, public address (0x), or ENS"]'))
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const addToAddressBookButton = await findElement(driver, By.css('.dialog.send__dialog.dialog--message'))
const addToAddressBookButton = await driver.findElement(By.css('.dialog.send__dialog.dialog--message'))
await addToAddressBookButton.click()
const addressBookAddModal = await driver.findElement(By.css('span .modal'))
await findElement(driver, By.css('.add-to-address-book-modal'))
const addressBookInput = await findElement(driver, By.css('.add-to-address-book-modal__input'))
await driver.findElement(By.css('.add-to-address-book-modal'))
const addressBookInput = await driver.findElement(By.css('.add-to-address-book-modal__input'))
await addressBookInput.sendKeys('Test Name 1')
await delay(tinyDelayMs)
const addressBookSaveButton = await findElement(driver, By.css('.add-to-address-book-modal__footer .btn-primary'))
await driver.delay(tinyDelayMs)
const addressBookSaveButton = await driver.findElement(By.css('.add-to-address-book-modal__footer .btn-primary'))
await addressBookSaveButton.click()
await driver.wait(until.stalenessOf(addressBookAddModal))
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys('1')
const inputValue = await inputAmount.getAttribute('value')
assert.equal(inputValue, '1')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('confirms the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirmButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(largeDelayMs * 2)
await driver.delay(largeDelayMs * 2)
})
it('finds the transaction in the transactions list', async function () {
await driver.wait(async () => {
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 1
}, 10000)
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
const txValues = await driver.findElement(By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/), 10000)
})
})
describe('Sends to an address book entry', () => {
it('starts a send transaction by clicking address book entry', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
const sendButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const recipientRow = await findElement(driver, By.css('.send__select-recipient-wrapper__group-item'))
const recipientRowTitle = await findElement(driver, By.css('.send__select-recipient-wrapper__group-item__title'))
const recipientRow = await driver.findElement(By.css('.send__select-recipient-wrapper__group-item'))
const recipientRowTitle = await driver.findElement(By.css('.send__select-recipient-wrapper__group-item__title'))
const recipientRowTitleString = await recipientRowTitle.getText()
assert.equal(recipientRowTitleString, 'Test Name 1')
await recipientRow.click()
await delay(regularDelayMs)
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
await driver.delay(regularDelayMs)
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys('2')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('confirms the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirmButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(largeDelayMs * 2)
await driver.delay(largeDelayMs * 2)
})
it('finds the transaction in the transactions list', async function () {
await driver.wait(async () => {
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 2
}, 10000)
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
const txValues = await driver.findElement(By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-2\s*ETH/), 10000)
})
})

@ -3,15 +3,9 @@ const webdriver = require('selenium-webdriver')
const { By, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
openNewPage,
verboseReportOnFailure,
waitUntilXWindowHandles,
switchToWindowWithTitle,
setupFetchMocking,
prepareExtensionForTesting,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -22,10 +16,6 @@ describe('MetaMask', function () {
let driver
let publicAddress
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -40,12 +30,11 @@ describe('MetaMask', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -53,7 +42,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -64,51 +53,51 @@ describe('MetaMask', function () {
describe('Going through the first time flow, but skipping the seed phrase challenge', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
const button = await findElement(driver, By.css('.first-time-flow__form button'))
const passwordBox = await driver.findElement(By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await driver.findElement(By.css('.first-time-flow__form #confirm-password'))
const button = await driver.findElement(By.css('.first-time-flow__form button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
await button.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('skips the seed phrase challenge', async () => {
const button = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
const button = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const detailsButton = await findElement(driver, By.css('.account-details__details-button'))
const detailsButton = await driver.findElement(By.css('.account-details__details-button'))
await detailsButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('gets the current accounts address', async () => {
const addressInput = await findElement(driver, By.css('.qr-ellip-address'))
const addressInput = await driver.findElement(By.css('.qr-ellip-address'))
publicAddress = await addressInput.getAttribute('value')
const accountModal = await driver.findElement(By.css('span .modal'))
@ -116,7 +105,7 @@ describe('MetaMask', function () {
await accountModalClose.click()
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
@ -127,69 +116,69 @@ describe('MetaMask', function () {
let dapp
it('connects to the dapp', async () => {
await openNewPage(driver, 'http://127.0.0.1:8080/')
await delay(regularDelayMs)
await driver.openNewPage('http://127.0.0.1:8080/')
await driver.delay(regularDelayMs)
const connectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
const connectButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Connect')]`))
await connectButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await waitUntilXWindowHandles(driver, 3)
await driver.waitUntilXWindowHandles(3)
const windowHandles = await driver.getAllWindowHandles()
extension = windowHandles[0]
dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles)
dapp = await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles)
popup = windowHandles.find(handle => handle !== extension && handle !== dapp)
await driver.switchTo().window(popup)
await driver.switchToWindow(popup)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const accountButton = await findElement(driver, By.css('.permissions-connect-choose-account__account'))
const accountButton = await driver.findElement(By.css('.permissions-connect-choose-account__account'))
await accountButton.click()
const submitButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Submit')]`))
const submitButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await submitButton.click()
await waitUntilXWindowHandles(driver, 2)
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
await driver.waitUntilXWindowHandles(2)
await driver.switchToWindow(dapp)
await driver.delay(regularDelayMs)
})
it('has the ganache network id within the dapp', async () => {
const networkDiv = await findElement(driver, By.css('#network'))
await delay(regularDelayMs)
const networkDiv = await driver.findElement(By.css('#network'))
await driver.delay(regularDelayMs)
assert.equal(await networkDiv.getText(), '5777')
})
it('changes the network', async () => {
await driver.switchTo().window(extension)
await driver.switchToWindow(extension)
const networkDropdown = await findElement(driver, By.css('.network-name'))
const networkDropdown = await driver.findElement(By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const ropstenButton = await findElement(driver, By.xpath(`//span[contains(text(), 'Ropsten')]`))
const ropstenButton = await driver.findElement(By.xpath(`//span[contains(text(), 'Ropsten')]`))
await ropstenButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('sets the network div within the dapp', async () => {
await driver.switchTo().window(dapp)
const networkDiv = await findElement(driver, By.css('#network'))
await driver.switchToWindow(dapp)
const networkDiv = await driver.findElement(By.css('#network'))
assert.equal(await networkDiv.getText(), '3')
})
it('sets the chainId div within the dapp', async () => {
await driver.switchTo().window(dapp)
const chainIdDiv = await findElement(driver, By.css('#chainId'))
await driver.switchToWindow(dapp)
const chainIdDiv = await driver.findElement(By.css('#chainId'))
assert.equal(await chainIdDiv.getText(), '0x3')
})
it('sets the account div within the dapp', async () => {
await driver.switchTo().window(dapp)
const accountsDiv = await findElement(driver, By.css('#accounts'))
await driver.switchToWindow(dapp)
const accountsDiv = await driver.findElement(By.css('#accounts'))
assert.equal(await accountsDiv.getText(), publicAddress.toLowerCase())
})
})

@ -3,13 +3,10 @@ const webdriver = require('selenium-webdriver')
const { By, Key, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
verboseReportOnFailure,
findElement,
findElements,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -23,9 +20,6 @@ describe('Using MetaMask with an existing account', function () {
const testAddress = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3'
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
const testPrivateKey3 = 'F4EC2590A0C10DE95FBF4547845178910E40F5035320C516A18C117DE02B5669'
const tinyDelayMs = 200
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -41,12 +35,11 @@ describe('Using MetaMask with an existing account', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -54,7 +47,7 @@ describe('Using MetaMask with an existing account', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -65,186 +58,191 @@ describe('Using MetaMask with an existing account', function () {
describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Import Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Import Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
const [seedTextArea] = await driver.findElements(By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [password] = await findElements(driver, By.id('password'))
const [password] = await driver.findElements(By.id('password'))
await password.sendKeys('correct horse battery staple')
const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
const [confirmPassword] = await driver.findElements(By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
const [importButton] = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Show account information', () => {
it('shows the correct account address', async () => {
await driver.findElement(By.css('.account-details__details-button')).click()
await driver.findElement(By.css('.qr-wrapper')).isDisplayed()
await delay(regularDelayMs)
const accountDetailsButton = await driver.findElement(By.css('.account-details__details-button'))
await accountDetailsButton.click()
await driver.findVisibleElement(By.css('.qr-wrapper'))
await driver.delay(regularDelayMs)
const [address] = await findElements(driver, By.css('input.qr-ellip-address'))
const [address] = await driver.findElements(By.css('input.qr-ellip-address'))
assert.equal(await address.getAttribute('value'), testAddress)
const accountModalClose = await driver.findElement(By.css('.account-modal-close'))
await accountModalClose.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('shows a QR code for the account', async () => {
await driver.findElement(By.css('.account-details__details-button')).click()
await driver.findElement(By.css('.qr-wrapper')).isDisplayed()
const accountDetailsButton = await driver.findElement(By.css('.account-details__details-button'))
await accountDetailsButton.click()
await driver.findVisibleElement(By.css('.qr-wrapper'))
const detailModal = await driver.findElement(By.css('span .modal'))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const accountModalClose = await driver.findElement(By.css('.account-modal-close'))
await accountModalClose.click()
await driver.wait(until.stalenessOf(detailModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Log out and log back in', () => {
it('logs out of the account', async () => {
const accountIdenticon = driver.findElement(By.css('.account-menu__icon .identicon'))
accountIdenticon.click()
await delay(regularDelayMs)
const accountIdenticon = await driver.findElement(By.css('.account-menu__icon .identicon'))
await accountIdenticon.click()
await driver.delay(regularDelayMs)
const [logoutButton] = await findElements(driver, By.css('.account-menu__logout-button'))
const [logoutButton] = await driver.findElements(By.css('.account-menu__logout-button'))
assert.equal(await logoutButton.getText(), 'Log out')
await logoutButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('accepts the account password after lock', async () => {
await driver.findElement(By.id('password')).sendKeys('correct horse battery staple')
await driver.findElement(By.id('password')).sendKeys(Key.ENTER)
await delay(largeDelayMs)
const passwordField = await driver.findElement(By.id('password'))
await passwordField.sendKeys('correct horse battery staple')
await passwordField.sendKeys(Key.ENTER)
await driver.delay(largeDelayMs)
})
})
describe('Add an account', () => {
it('switches to localhost', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
const networkDropdown = await driver.findElement(By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
const [localhost] = await driver.findElements(By.xpath(`//span[contains(text(), 'Localhost')]`))
await localhost.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('choose Create Account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const [createAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Create Account')]`))
const [createAccount] = await driver.findElements(By.xpath(`//div[contains(text(), 'Create Account')]`))
await createAccount.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('set account name', async () => {
const [accountName] = await findElements(driver, By.css('.new-account-create-form input'))
const [accountName] = await driver.findElements(By.css('.new-account-create-form input'))
await accountName.sendKeys('2nd account')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [createButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Create')]`))
const [createButton] = await driver.findElements(By.xpath(`//button[contains(text(), 'Create')]`))
await createButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should show the correct account name', async () => {
const [accountName] = await findElements(driver, By.css('.account-details__account-name'))
const [accountName] = await driver.findElements(By.css('.account-details__account-name'))
assert.equal(await accountName.getText(), '2nd account')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Switch back to original account', () => {
it('chooses the original account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const [originalAccountMenuItem] = await findElements(driver, By.css('.account-menu__name'))
const [originalAccountMenuItem] = await driver.findElements(By.css('.account-menu__name'))
await originalAccountMenuItem.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Send ETH from inside MetaMask', () => {
it('starts a send transaction', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
const sendButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const inputAddress = await findElement(driver, By.css('input[placeholder="Search, public address (0x), or ENS"]'))
const inputAddress = await driver.findElement(By.css('input[placeholder="Search, public address (0x), or ENS"]'))
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys('1')
// Set the gas limit
const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn'))
const configureGas = await driver.findElement(By.css('.advanced-gas-options-btn'))
await configureGas.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
const save = await driver.findElement(By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('confirms the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirmButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('finds the transaction in the transactions list', async function () {
await driver.wait(async () => {
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 1
}, 10000)
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
const txValues = await driver.findElements(By.css('.transaction-list-item__amount--primary'))
assert.equal(txValues.length, 1)
assert.ok(/-1\s*ETH/.test(await txValues[0].getText()))
})
@ -252,102 +250,105 @@ describe('Using MetaMask with an existing account', function () {
describe('Imports an account with private key', () => {
it('choose Create Account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const [importAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Import Account')]`))
const [importAccount] = await driver.findElements(By.xpath(`//div[contains(text(), 'Import Account')]`))
await importAccount.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('enter private key', async () => {
const privateKeyInput = await findElement(driver, By.css('#private-key-box'))
const privateKeyInput = await driver.findElement(By.css('#private-key-box'))
await privateKeyInput.sendKeys(testPrivateKey2)
await delay(regularDelayMs)
const importButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
await driver.delay(regularDelayMs)
const importButtons = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButtons[0].click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should show the correct account name', async () => {
const [accountName] = await findElements(driver, By.css('.account-details__account-name'))
const [accountName] = await driver.findElements(By.css('.account-details__account-name'))
assert.equal(await accountName.getText(), 'Account 4')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should show the imported label', async () => {
const [importedLabel] = await findElements(driver, By.css('.account-details__keyring-label'))
const [importedLabel] = await driver.findElements(By.css('.account-details__keyring-label'))
assert.equal(await importedLabel.getText(), 'IMPORTED')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Imports and removes an account', () => {
it('choose Create Account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const [importAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Import Account')]`))
const [importAccount] = await driver.findElements(By.xpath(`//div[contains(text(), 'Import Account')]`))
await importAccount.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('enter private key', async () => {
const privateKeyInput = await findElement(driver, By.css('#private-key-box'))
const privateKeyInput = await driver.findElement(By.css('#private-key-box'))
await privateKeyInput.sendKeys(testPrivateKey3)
await delay(regularDelayMs)
const importButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
await driver.delay(regularDelayMs)
const importButtons = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButtons[0].click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should open the remove account modal', async () => {
const [accountName] = await findElements(driver, By.css('.account-details__account-name'))
const [accountName] = await driver.findElements(By.css('.account-details__account-name'))
assert.equal(await accountName.getText(), 'Account 5')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const accountListItems = await findElements(driver, By.css('.account-menu__account'))
const accountListItems = await driver.findElements(By.css('.account-menu__account'))
assert.equal(accountListItems.length, 5)
const removeAccountIcons = await findElements(driver, By.css('.remove-account-icon'))
const removeAccountIcons = await driver.findElements(By.css('.remove-account-icon'))
await removeAccountIcons[1].click()
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
await findElement(driver, By.css('.confirm-remove-account__account'))
await driver.findElement(By.css('.confirm-remove-account__account'))
})
it('should remove the account', async () => {
const removeButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Remove')]`))
const removeButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Remove')]`))
await removeButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [accountName] = await findElements(driver, By.css('.account-details__account-name'))
const [accountName] = await driver.findElements(By.css('.account-details__account-name'))
assert.equal(await accountName.getText(), 'Account 1')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const accountListItems = await findElements(driver, By.css('.account-menu__account'))
const accountListItems = await driver.findElements(By.css('.account-menu__account'))
assert.equal(accountListItems.length, 4)
})
})
describe('Connects to a Hardware wallet', () => {
it('choose Connect Hardware Wallet from the account menu', async () => {
const [connectAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Connect Hardware Wallet')]`))
const [connectAccount] = await driver.findElements(By.xpath(`//div[contains(text(), 'Connect Hardware Wallet')]`))
await connectAccount.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should open the TREZOR Connect popup', async () => {
const trezorButton = await findElements(driver, By.css('.hw-connect__btn'))
const trezorButton = await driver.findElements(By.css('.hw-connect__btn'))
await trezorButton[1].click()
await delay(regularDelayMs)
const connectButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
await driver.delay(regularDelayMs)
const connectButtons = await driver.findElements(By.xpath(`//button[contains(text(), 'Connect')]`))
await connectButtons[0].click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const allWindows = await driver.getAllWindowHandles()
assert.equal(allWindows.length, 2)
})

@ -1,28 +1,10 @@
const fs = require('fs')
const mkdirp = require('mkdirp')
const pify = require('pify')
const assert = require('assert')
const { until } = require('selenium-webdriver')
const { buildWebDriver } = require('./webdriver')
const fetchMockResponses = require('./fetch-mocks.json')
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
module.exports = {
assertElementNotPresent,
checkBrowserForConsoleErrors,
closeAllWindowHandlesExcept,
delay,
findElement,
findElements,
openNewPage,
switchToWindowWithTitle,
verboseReportOnFailure,
waitUntilXWindowHandles,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
@ -34,143 +16,5 @@ async function prepareExtensionForTesting ({ responsive, port } = {}) {
const extensionPath = `dist/${browser}`
const { driver, extensionId, extensionUrl } = await buildWebDriver({ browser, extensionPath, responsive, port })
await driver.get(extensionUrl)
return { driver, extensionId, extensionUrl }
}
async function setupFetchMocking (driver) {
// define fetchMocking script, to be evaluated in the browser
function fetchMocking (fetchMockResponses) {
window.origFetch = window.fetch.bind(window)
window.fetch = async (...args) => {
const url = args[0]
if (url === 'https://ethgasstation.info/json/ethgasAPI.json') {
return { json: async () => clone(fetchMockResponses.ethGasBasic) }
} else if (url === 'https://ethgasstation.info/json/predictTable.json') {
return { json: async () => clone(fetchMockResponses.ethGasPredictTable) }
} else if (url.match(/chromeextensionmm/)) {
return { json: async () => clone(fetchMockResponses.metametrics) }
}
return window.origFetch(...args)
}
if (window.chrome && window.chrome.webRequest) {
window.chrome.webRequest.onBeforeRequest.addListener(cancelInfuraRequest, { urls: ['https://*.infura.io/*'] }, ['blocking'])
}
function cancelInfuraRequest (requestDetails) {
console.log(`fetchMocking - Canceling request: "${requestDetails.url}"`)
return { cancel: true }
}
function clone (obj) {
return JSON.parse(JSON.stringify(obj))
}
}
// fetchMockResponses are parsed last minute to ensure that objects are uniquely instantiated
const fetchMockResponsesJson = JSON.stringify(fetchMockResponses)
// eval the fetchMocking script in the browser
await driver.executeScript(`(${fetchMocking})(${fetchMockResponsesJson})`)
}
async function checkBrowserForConsoleErrors (driver) {
const ignoredLogTypes = ['WARNING']
const ignoredErrorMessages = [
// Third-party Favicon 404s show up as errors
'favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)',
]
const browserLogs = await driver.manage().logs().get('browser')
const errorEntries = browserLogs.filter(entry => !ignoredLogTypes.includes(entry.level.toString()))
const errorObjects = errorEntries.map(entry => entry.toJSON())
return errorObjects.filter(entry => !ignoredErrorMessages.some(message => entry.message.includes(message)))
}
async function verboseReportOnFailure (driver, test) {
let artifactDir
if (process.env.SELENIUM_BROWSER === 'chrome') {
artifactDir = `./test-artifacts/chrome/${test.title}`
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
artifactDir = `./test-artifacts/firefox/${test.title}`
}
const filepathBase = `${artifactDir}/test-failure`
await pify(mkdirp)(artifactDir)
const screenshot = await driver.takeScreenshot()
await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
const htmlSource = await driver.getPageSource()
await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource)
}
function delay (time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function findElement (driver, by, timeout = 10000) {
return driver.wait(until.elementLocated(by), timeout)
}
async function findElements (driver, by, timeout = 10000) {
return driver.wait(until.elementsLocated(by), timeout)
}
async function openNewPage (driver, url) {
const newHandle = await driver.switchTo().newWindow()
await driver.get(url)
return newHandle
}
async function waitUntilXWindowHandles (driver, x, delayStep = 1000, timeout = 5000) {
let timeElapsed = 0
while (timeElapsed <= timeout) {
const windowHandles = await driver.getAllWindowHandles()
if (windowHandles.length === x) {
return
}
await delay(delayStep)
timeElapsed += delayStep
}
throw new Error('waitUntilXWindowHandles timed out polling window handles')
}
async function switchToWindowWithTitle (driver, title, windowHandles) {
if (!windowHandles) {
windowHandles = await driver.getAllWindowHandles()
}
for (const handle of windowHandles) {
await driver.switchTo().window(handle)
const handleTitle = await driver.getTitle()
if (handleTitle === title) {
return handle
}
}
throw new Error('No window with title: ' + title)
}
/**
* Closes all windows except those in the given list of exceptions
* @param {object} driver the WebDriver instance
* @param {Array<string>} exceptions the list of window handle exceptions
* @param {Array?} windowHandles the full list of window handles
* @returns {Promise<void>}
*/
async function closeAllWindowHandlesExcept (driver, exceptions, windowHandles) {
windowHandles = windowHandles || await driver.getAllWindowHandles()
for (const handle of windowHandles) {
if (!exceptions.includes(handle)) {
await driver.switchTo().window(handle)
await delay(1000)
await driver.close()
await delay(1000)
}
}
}
async function assertElementNotPresent (webdriver, driver, by) {
let dataTab
try {
dataTab = await findElement(driver, by, 4000)
} catch (err) {
assert(err instanceof webdriver.error.NoSuchElementError || err instanceof webdriver.error.TimeoutError)
}
assert.ok(!dataTab, 'Found element that should not be present')
}

@ -3,15 +3,10 @@ const webdriver = require('selenium-webdriver')
const { By, until } = webdriver
const {
assertElementNotPresent,
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
openNewPage,
verboseReportOnFailure,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -22,10 +17,6 @@ describe('MetaMask', function () {
let driver
let publicAddress
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -44,12 +35,11 @@ describe('MetaMask', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -57,7 +47,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -68,51 +58,51 @@ describe('MetaMask', function () {
describe('Going through the first time flow, but skipping the seed phrase challenge', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
const button = await findElement(driver, By.css('.first-time-flow__form button'))
const passwordBox = await driver.findElement(By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await driver.findElement(By.css('.first-time-flow__form #confirm-password'))
const button = await driver.findElement(By.css('.first-time-flow__form button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('skips the seed phrase challenge', async () => {
const button = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
const button = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const detailsButton = await findElement(driver, By.css('.account-details__details-button'))
const detailsButton = await driver.findElement(By.css('.account-details__details-button'))
await detailsButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('gets the current accounts address', async () => {
const addressInput = await findElement(driver, By.css('.qr-ellip-address'))
const addressInput = await driver.findElement(By.css('.qr-ellip-address'))
publicAddress = await addressInput.getAttribute('value')
const accountModal = await driver.findElement(By.css('span .modal'))
@ -121,7 +111,7 @@ describe('MetaMask', function () {
await accountModalClose.click()
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
@ -133,28 +123,28 @@ describe('MetaMask', function () {
const windowHandles = await driver.getAllWindowHandles()
extension = windowHandles[0]
await openNewPage(driver, 'http://127.0.0.1:8080/')
await delay(regularDelayMs)
await driver.openNewPage('http://127.0.0.1:8080/')
await driver.delay(regularDelayMs)
})
it('sends eth to the current account', async () => {
const addressInput = await findElement(driver, By.css('#address'))
const addressInput = await driver.findElement(By.css('#address'))
await addressInput.sendKeys(publicAddress)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const sendButton = await findElement(driver, By.css('#send'))
const sendButton = await driver.findElement(By.css('#send'))
await sendButton.click()
const txStatus = await findElement(driver, By.css('#success'))
const txStatus = await driver.findElement(By.css('#success'))
await driver.wait(until.elementTextMatches(txStatus, /Success/), 15000)
})
it('switches back to MetaMask', async () => {
await driver.switchTo().window(extension)
await driver.switchToWindow(extension)
})
it('should have the correct amount of eth', async () => {
const balances = await findElements(driver, By.css('.currency-display-component__text'))
const balances = await driver.findElements(By.css('.currency-display-component__text'))
await driver.wait(until.elementTextMatches(balances[0], /1/), 15000)
const balance = await balances[0].getText()
@ -164,40 +154,40 @@ describe('MetaMask', function () {
describe('backs up the seed phrase', () => {
it('should show a backup reminder', async () => {
const backupReminder = await findElements(driver, By.xpath("//div[contains(@class, 'home-notification__text') and contains(text(), 'Backup your Secret Recovery code to keep your wallet and funds secure')]"))
const backupReminder = await driver.findElements(By.xpath("//div[contains(@class, 'home-notification__text') and contains(text(), 'Backup your Secret Recovery code to keep your wallet and funds secure')]"))
assert.equal(backupReminder.length, 1)
})
it('should take the user to the seedphrase backup screen', async () => {
const backupButton = await findElement(driver, By.css('.home-notification__accept-button'))
const backupButton = await driver.findElement(By.css('.home-notification__accept-button'))
await backupButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
let seedPhrase
it('reveals the seed phrase', async () => {
const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
const revealSeedPhraseButton = await driver.findElement(byRevealButton)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText()
const revealedSeedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words'))
seedPhrase = await revealedSeedPhrase.getText()
assert.equal(seedPhrase.split(' ').length, 12)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
async function clickWordAndWait (word) {
const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]`
const word0 = await findElement(driver, By.xpath(xpath), 10000)
const word0 = await driver.findElement(By.xpath(xpath))
await word0.click()
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
}
it('can retype the seed phrase', async () => {
@ -207,19 +197,19 @@ describe('MetaMask', function () {
await clickWordAndWait(word)
}
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirm = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('can click through the success screen', async () => {
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'All Done')]`))
const confirm = await driver.findElement(By.xpath(`//button[contains(text(), 'All Done')]`))
await confirm.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('should have the correct amount of eth', async () => {
const balances = await findElements(driver, By.css('.currency-display-component__text'))
const balances = await driver.findElements(By.css('.currency-display-component__text'))
await driver.wait(until.elementTextMatches(balances[0], /1/), 15000)
const balance = await balances[0].getText()
@ -227,7 +217,7 @@ describe('MetaMask', function () {
})
it('should not show a backup reminder', async () => {
await assertElementNotPresent(webdriver, driver, By.css('.backup-notification'))
await driver.assertElementNotPresent(By.css('.backup-notification'))
})
})
})

@ -3,13 +3,10 @@ const webdriver = require('selenium-webdriver')
const { By, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
verboseReportOnFailure,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -20,9 +17,6 @@ describe('MetaMask', function () {
let driver
const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -31,12 +25,11 @@ describe('MetaMask', function () {
await ganacheServer.start()
const result = await prepareExtensionForTesting({ responsive: true })
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -44,7 +37,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -55,63 +48,63 @@ describe('MetaMask', function () {
describe('Going through the first time flow', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "I agree" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-primary'))
const optOutButton = await driver.findElement(By.css('.btn-primary'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
const button = await findElement(driver, By.css('.first-time-flow__form button'))
const passwordBox = await driver.findElement(By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await driver.findElement(By.css('.first-time-flow__form #confirm-password'))
const button = await driver.findElement(By.css('.first-time-flow__form button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
let seedPhrase
it('reveals the seed phrase', async () => {
const byRevealButton = By.css('.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
const revealSeedPhraseButton = await driver.findElement(byRevealButton)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
seedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words')).getText()
const revealedSeedPhrase = await driver.findElement(By.css('.reveal-seed-phrase__secret-words'))
seedPhrase = await revealedSeedPhrase.getText()
assert.equal(seedPhrase.split(' ').length, 12)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.next.message}')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
async function clickWordAndWait (word) {
const xpath = `//div[contains(@class, 'confirm-seed-phrase__seed-word--shuffled') and not(contains(@class, 'confirm-seed-phrase__seed-word--selected')) and contains(text(), '${word}')]`
const word0 = await findElement(driver, By.xpath(xpath), 10000)
const word0 = await driver.findElement(By.xpath(xpath))
await word0.click()
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
}
it('can retype the seed phrase', async () => {
@ -121,126 +114,129 @@ describe('MetaMask', function () {
await clickWordAndWait(word)
}
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirm = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Show account information', () => {
it('show account details dropdown menu', async () => {
await driver.findElement(By.css('div.menu-bar__open-in-browser')).click()
const openInBrowser = await driver.findElement(By.css('div.menu-bar__open-in-browser'))
await openInBrowser.click()
const options = await driver.findElements(By.css('div.menu.account-details-dropdown div.menu__item'))
assert.equal(options.length, 4) // HD Wallet type does not have to show the Remove Account option
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Import seed phrase', () => {
it('logs out of the vault', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const logoutButton = await findElement(driver, By.css('.account-menu__logout-button'))
const logoutButton = await driver.findElement(By.css('.account-menu__logout-button'))
assert.equal(await logoutButton.getText(), 'Log out')
await logoutButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('imports seed phrase', async () => {
const restoreSeedLink = await findElement(driver, By.css('.unlock-page__link--import'))
const restoreSeedLink = await driver.findElement(By.css('.unlock-page__link--import'))
assert.equal(await restoreSeedLink.getText(), 'Import using account seed phrase')
await restoreSeedLink.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const seedTextArea = await findElement(driver, By.css('textarea'))
const seedTextArea = await driver.findElement(By.css('textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const passwordInputs = await driver.findElements(By.css('input'))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await passwordInputs[0].sendKeys('correct horse battery staple')
await passwordInputs[1].sendKeys('correct horse battery staple')
await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.restore.message}')]`)).click()
await delay(regularDelayMs)
const restoreButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.restore.message}')]`))
await restoreButton.click()
await driver.delay(regularDelayMs)
})
it('switches to localhost', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
const networkDropdown = await driver.findElement(By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
const [localhost] = await driver.findElements(By.xpath(`//span[contains(text(), 'Localhost')]`))
await localhost.click()
await delay(largeDelayMs * 2)
await driver.delay(largeDelayMs * 2)
})
it('balance renders', async () => {
const balance = await findElement(driver, By.css('.transaction-view-balance__primary-balance'))
const balance = await driver.findElement(By.css('.transaction-view-balance__primary-balance'))
await driver.wait(until.elementTextMatches(balance, /100\s*ETH/))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Send ETH from inside MetaMask', () => {
it('starts to send a transaction', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
const sendButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const inputAddress = await findElement(driver, By.css('input[placeholder="Search, public address (0x), or ENS"]'))
const inputAddress = await driver.findElement(By.css('input[placeholder="Search, public address (0x), or ENS"]'))
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys('1')
const inputValue = await inputAmount.getAttribute('value')
assert.equal(inputValue, '1')
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('opens and closes the gas modal', async function () {
// Set the gas limit
const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn'))
const configureGas = await driver.findElement(By.css('.advanced-gas-options-btn'))
await configureGas.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const save = await findElement(driver, By.css('.page-container__header-close-text'))
const save = await driver.findElement(By.css('.page-container__header-close-text'))
await save.click()
await driver.wait(until.stalenessOf(gasModal), 10000)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through to the confirm screen', async function () {
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('confirms the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirmButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('finds the transaction in the transactions list', async function () {
await driver.wait(async () => {
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 1
}, 10000)
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
const txValues = await driver.findElement(By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/), 10000)
})
})

File diff suppressed because it is too large Load Diff

@ -3,16 +3,9 @@ const webdriver = require('selenium-webdriver')
const { By, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
openNewPage,
verboseReportOnFailure,
waitUntilXWindowHandles,
switchToWindowWithTitle,
setupFetchMocking,
prepareExtensionForTesting,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -23,10 +16,6 @@ describe('MetaMask', function () {
let driver
let publicAddress
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -41,12 +30,11 @@ describe('MetaMask', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -54,7 +42,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -65,51 +53,51 @@ describe('MetaMask', function () {
describe('Going through the first time flow, but skipping the seed phrase challenge', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Create New Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Create a Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
const button = await findElement(driver, By.css('.first-time-flow__form button'))
const passwordBox = await driver.findElement(By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await driver.findElement(By.css('.first-time-flow__form #confirm-password'))
const button = await driver.findElement(By.css('.first-time-flow__form button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
await button.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('skips the seed phrase challenge', async () => {
const button = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
const button = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.remindMeLater.message}')]`))
await button.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const detailsButton = await findElement(driver, By.css('.account-details__details-button'))
const detailsButton = await driver.findElement(By.css('.account-details__details-button'))
await detailsButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('gets the current accounts address', async () => {
const addressInput = await findElement(driver, By.css('.qr-ellip-address'))
const addressInput = await driver.findElement(By.css('.qr-ellip-address'))
publicAddress = await addressInput.getAttribute('value')
const accountModal = await driver.findElement(By.css('span .modal'))
@ -117,7 +105,7 @@ describe('MetaMask', function () {
await accountModalClose.click()
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
@ -127,86 +115,86 @@ describe('MetaMask', function () {
let dapp
it('connects to the dapp', async () => {
await openNewPage(driver, 'http://127.0.0.1:8080/')
await delay(regularDelayMs)
await driver.openNewPage('http://127.0.0.1:8080/')
await driver.delay(regularDelayMs)
const connectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
const connectButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Connect')]`))
await connectButton.click()
await waitUntilXWindowHandles(driver, 3)
await driver.waitUntilXWindowHandles(3)
const windowHandles = await driver.getAllWindowHandles()
extension = windowHandles[0]
dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles)
dapp = await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles)
popup = windowHandles.find(handle => handle !== extension && handle !== dapp)
await driver.switchTo().window(popup)
await driver.switchToWindow(popup)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const accountButton = await findElement(driver, By.css('.permissions-connect-choose-account__account'))
const accountButton = await driver.findElement(By.css('.permissions-connect-choose-account__account'))
await accountButton.click()
const submitButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Submit')]`))
const submitButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await submitButton.click()
await waitUntilXWindowHandles(driver, 2)
await driver.switchTo().window(extension)
await delay(regularDelayMs)
await driver.waitUntilXWindowHandles(2)
await driver.switchToWindow(extension)
await driver.delay(regularDelayMs)
})
it('shows connected sites', async () => {
const connectedSites = await findElement(driver, By.xpath(`//button[contains(text(), 'Connected Sites')]`))
const connectedSites = await driver.findElement(By.xpath(`//button[contains(text(), 'Connected Sites')]`))
await connectedSites.click()
await findElement(driver, By.css('.connected-sites__title'))
await driver.findElement(By.css('.connected-sites__title'))
const domains = await findElements(driver, By.css('.connected-sites-list__domain'))
const domains = await driver.findElements(By.css('.connected-sites-list__domain'))
assert.equal(domains.length, 1)
const domainName = await findElement(driver, By.css('.connected-sites-list__domain-name'))
const domainName = await driver.findElement(By.css('.connected-sites-list__domain-name'))
assert.equal(await domainName.getText(), 'E2E Test Dapp')
await domains[0].click()
const permissionDescription = await findElement(driver, By.css('.connected-sites-list__permission-description'))
const permissionDescription = await driver.findElement(By.css('.connected-sites-list__permission-description'))
assert.equal(await permissionDescription.getText(), 'View the address of the selected account')
})
it('can get accounts within the dapp', async () => {
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
await driver.switchToWindow(dapp)
await driver.delay(regularDelayMs)
const getAccountsButton = await findElement(driver, By.xpath(`//button[contains(text(), 'eth_accounts')]`))
const getAccountsButton = await driver.findElement(By.xpath(`//button[contains(text(), 'eth_accounts')]`))
await getAccountsButton.click()
const getAccountsResult = await findElement(driver, By.css('#getAccountsResult'))
const getAccountsResult = await driver.findElement(By.css('#getAccountsResult'))
assert.equal((await getAccountsResult.getText()).toLowerCase(), publicAddress.toLowerCase())
})
it('can disconnect all accounts', async () => {
await driver.switchTo().window(extension)
await driver.switchToWindow(extension)
const disconnectAllButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Disconnect All')]`))
const disconnectAllButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Disconnect All')]`))
await disconnectAllButton.click()
const disconnectModal = await driver.findElement(By.css('span .modal'))
const disconnectAllModalButton = await findElement(driver, By.css('.disconnect-all-modal .btn-danger'))
const disconnectAllModalButton = await driver.findElement(By.css('.disconnect-all-modal .btn-danger'))
await disconnectAllModalButton.click()
await driver.wait(until.stalenessOf(disconnectModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('can no longer get accounts within the dapp', async () => {
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
await driver.switchToWindow(dapp)
await driver.delay(regularDelayMs)
const getAccountsButton = await findElement(driver, By.xpath(`//button[contains(text(), 'eth_accounts')]`))
const getAccountsButton = await driver.findElement(By.xpath(`//button[contains(text(), 'eth_accounts')]`))
await getAccountsButton.click()
const getAccountsResult = await findElement(driver, By.css('#getAccountsResult'))
const getAccountsResult = await driver.findElement(By.css('#getAccountsResult'))
assert.equal(await getAccountsResult.getText(), 'Not able to get accounts')
})
})

@ -3,13 +3,10 @@ const webdriver = require('selenium-webdriver')
const { By, Key, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
verboseReportOnFailure,
findElement,
findElements,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -20,9 +17,6 @@ describe('Using MetaMask with an existing account', function () {
let driver
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
const tinyDelayMs = 200
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -38,12 +32,11 @@ describe('Using MetaMask with an existing account', function () {
})
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -51,7 +44,7 @@ describe('Using MetaMask with an existing account', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -62,102 +55,102 @@ describe('Using MetaMask with an existing account', function () {
describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Import Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Import Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
const [seedTextArea] = await driver.findElements(By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [password] = await findElements(driver, By.id('password'))
const [password] = await driver.findElements(By.id('password'))
await password.sendKeys('correct horse battery staple')
const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
const [confirmPassword] = await driver.findElements(By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
const [importButton] = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('Send ETH from inside MetaMask', () => {
it('starts a send transaction', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
const sendButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const inputAddress = await findElement(driver, By.css('input[placeholder="Search, public address (0x), or ENS"]'))
const inputAddress = await driver.findElement(By.css('input[placeholder="Search, public address (0x), or ENS"]'))
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys('1')
// Set the gas limit
const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn'))
const configureGas = await driver.findElement(By.css('.advanced-gas-options-btn'))
await configureGas.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-gas-inputs__gas-edit-row__input'))
const [gasPriceInput, gasLimitInput] = await driver.findElements(By.css('.advanced-gas-inputs__gas-edit-row__input'))
await gasPriceInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys(Key.BACK_SPACE)
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys(Key.BACK_SPACE)
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys('10')
await delay(50)
await delay(tinyDelayMs)
await delay(50)
await driver.delay(50)
await driver.delay(tinyDelayMs)
await driver.delay(50)
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
await delay(50)
await driver.delay(50)
await gasLimitInput.sendKeys('25000')
await delay(1000)
await driver.delay(1000)
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
const save = await driver.findElement(By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('has correct value and fee on the confirm screen the transaction', async function () {
const transactionAmounts = await findElements(driver, By.css('.currency-display-component__text'))
const transactionAmounts = await driver.findElements(By.css('.currency-display-component__text'))
const transactionAmount = transactionAmounts[0]
assert.equal(await transactionAmount.getText(), '1')
@ -166,55 +159,55 @@ describe('Using MetaMask with an existing account', function () {
})
it('edits the transaction', async function () {
const editButton = await findElement(driver, By.css('.confirm-page-container-header__back-button'))
const editButton = await driver.findElement(By.css('.confirm-page-container-header__back-button'))
await editButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
const inputAmount = await driver.findElement(By.css('.unit-input__input'))
await inputAmount.sendKeys(Key.chord(Key.CONTROL, 'a'))
await delay(50)
await driver.delay(50)
await inputAmount.sendKeys(Key.BACK_SPACE)
await delay(50)
await driver.delay(50)
await inputAmount.sendKeys('2.2')
const configureGas = await findElement(driver, By.css('.advanced-gas-options-btn'))
const configureGas = await driver.findElement(By.css('.advanced-gas-options-btn'))
await configureGas.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-gas-inputs__gas-edit-row__input'))
const [gasPriceInput, gasLimitInput] = await driver.findElements(By.css('.advanced-gas-inputs__gas-edit-row__input'))
await gasPriceInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys(Key.BACK_SPACE)
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys(Key.BACK_SPACE)
await delay(50)
await driver.delay(50)
await gasPriceInput.sendKeys('8')
await delay(50)
await delay(tinyDelayMs)
await delay(50)
await driver.delay(50)
await driver.delay(tinyDelayMs)
await driver.delay(50)
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
await delay(50)
await driver.delay(50)
await gasLimitInput.sendKeys('100000')
await delay(1000)
await driver.delay(1000)
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
const save = await driver.findElement(By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
const nextScreen = await driver.findElement(By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('has correct updated value on the confirm screen the transaction', async function () {
const transactionAmounts = await findElements(driver, By.css('.currency-display-component__text'))
const transactionAmounts = await driver.findElements(By.css('.currency-display-component__text'))
const transactionAmount = transactionAmounts[0]
assert.equal(await transactionAmount.getText(), '2.2')
@ -223,18 +216,18 @@ describe('Using MetaMask with an existing account', function () {
})
it('confirms the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
const confirmButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('finds the transaction in the transactions list', async function () {
await driver.wait(async () => {
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 1
}, 10000)
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
const txValues = await driver.findElements(By.css('.transaction-list-item__amount--primary'))
assert.equal(txValues.length, 1)
assert.ok(/-2.2\s*ETH/.test(await txValues[0].getText()))
})

@ -4,16 +4,9 @@ const webdriver = require('selenium-webdriver')
const { By, Key, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
openNewPage,
verboseReportOnFailure,
waitUntilXWindowHandles,
switchToWindowWithTitle,
setupFetchMocking,
prepareExtensionForTesting,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const FixtureServer = require('./fixture-server')
@ -26,10 +19,6 @@ describe('MetaMask', function () {
let driver
let publicAddress
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -40,12 +29,11 @@ describe('MetaMask', function () {
publicAddress = '0x5cfe73b6021e818b776b421b1c4db2474086a7e1'
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -53,7 +41,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -70,55 +58,56 @@ describe('MetaMask', function () {
let windowHandles
it('accepts the account password after lock', async () => {
await delay(1000)
await driver.findElement(By.id('password')).sendKeys('correct horse battery staple')
await driver.findElement(By.id('password')).sendKeys(Key.ENTER)
await delay(largeDelayMs * 4)
await driver.delay(1000)
const passwordField = await driver.findElement(By.id('password'))
await passwordField.sendKeys('correct horse battery staple')
await passwordField.sendKeys(Key.ENTER)
await driver.delay(largeDelayMs * 4)
})
it('connects to the dapp', async () => {
await openNewPage(driver, 'http://127.0.0.1:8080/')
await delay(regularDelayMs)
await driver.openNewPage('http://127.0.0.1:8080/')
await driver.delay(regularDelayMs)
const connectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
const connectButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Connect')]`))
await connectButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await waitUntilXWindowHandles(driver, 3)
await driver.waitUntilXWindowHandles(3)
const windowHandles = await driver.getAllWindowHandles()
extension = windowHandles[0]
dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles)
dapp = await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles)
popup = windowHandles.find(handle => handle !== extension && handle !== dapp)
await driver.switchTo().window(popup)
await driver.switchToWindow(popup)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const accountButton = await findElement(driver, By.css('.permissions-connect-choose-account__account'))
const accountButton = await driver.findElement(By.css('.permissions-connect-choose-account__account'))
await accountButton.click()
const submitButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Submit')]`))
const submitButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Submit')]`))
await submitButton.click()
await waitUntilXWindowHandles(driver, 2)
await driver.switchTo().window(dapp)
await driver.waitUntilXWindowHandles(2)
await driver.switchToWindow(dapp)
})
it('creates a sign typed data signature request', async () => {
const signTypedMessage = await findElement(driver, By.xpath(`//button[contains(text(), 'Sign')]`), 10000)
const signTypedMessage = await driver.findElement(By.xpath(`//button[contains(text(), 'Sign')]`), 10000)
await signTypedMessage.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
windowHandles = await driver.getAllWindowHandles()
await switchToWindowWithTitle(driver, 'MetaMask Notification', windowHandles)
await delay(regularDelayMs)
await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles)
await driver.delay(regularDelayMs)
const title = await findElement(driver, By.css('.signature-request-content__title'))
const name = await findElement(driver, By.css('.signature-request-content__info--bolded'))
const content = await findElements(driver, By.css('.signature-request-content__info'))
const title = await driver.findElement(By.css('.signature-request-content__title'))
const name = await driver.findElement(By.css('.signature-request-content__info--bolded'))
const content = await driver.findElements(By.css('.signature-request-content__info'))
const origin = content[0]
const address = content[1]
assert.equal(await title.getText(), 'Signature Request')
@ -128,20 +117,20 @@ describe('MetaMask', function () {
})
it('signs the transaction', async () => {
const signButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Sign')]`), 10000)
const signButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Sign')]`), 10000)
await signButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
extension = windowHandles[0]
await driver.switchTo().window(extension)
await driver.switchToWindow(extension)
})
it('gets the current accounts address', async () => {
const detailsButton = await findElement(driver, By.css('.account-details__details-button'))
const detailsButton = await driver.findElement(By.css('.account-details__details-button'))
await detailsButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const addressInput = await findElement(driver, By.css('.qr-ellip-address'))
const addressInput = await driver.findElement(By.css('.qr-ellip-address'))
const newPublicAddress = await addressInput.getAttribute('value')
const accountModal = await driver.findElement(By.css('span .modal'))
@ -149,7 +138,7 @@ describe('MetaMask', function () {
await accountModalClose.click()
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
assert.equal(newPublicAddress.toLowerCase(), publicAddress)
})
})

@ -4,13 +4,10 @@ const getPort = require('get-port')
const { By, until } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
verboseReportOnFailure,
setupFetchMocking,
prepareExtensionForTesting,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const Ganache = require('./ganache')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -21,9 +18,6 @@ describe('MetaMask', function () {
let driver
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
@ -39,12 +33,11 @@ describe('MetaMask', function () {
})
const result = await prepareExtensionForTesting({ port: await getPort() })
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -52,7 +45,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -65,70 +58,71 @@ describe('MetaMask', function () {
describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Import Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Import Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
const [seedTextArea] = await driver.findElements(By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [password] = await findElements(driver, By.id('password'))
const [password] = await driver.findElements(By.id('password'))
await password.sendKeys('correct horse battery staple')
const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
const [confirmPassword] = await driver.findElements(By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
const [importButton] = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('balance renders', async () => {
const balance = await findElement(driver, By.css('.balance-display .token-amount'))
const balance = await driver.findElement(By.css('.balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /25\s*ETH/))
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
describe('turns on threebox syncing', () => {
it('goes to the settings screen', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`))
const settingsButton = await driver.findElement(By.xpath(`//div[contains(text(), 'Settings')]`))
settingsButton.click()
})
it('turns on threebox syncing', async () => {
const advancedButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Advanced')]`))
const advancedButton = await driver.findElement(By.xpath(`//div[contains(text(), 'Advanced')]`))
await advancedButton.click()
const threeBoxToggle = await findElements(driver, By.css('.toggle-button'))
const threeBoxToggle = await driver.findElements(By.css('.toggle-button'))
const threeBoxToggleButton = await threeBoxToggle[4].findElement(By.css('div'))
await threeBoxToggleButton.click()
})
@ -137,37 +131,37 @@ describe('MetaMask', function () {
describe('updates settings and address book', () => {
it('adds an address to the contact list', async () => {
const generalButton = await findElement(driver, By.xpath(`//div[contains(text(), 'General')]`))
const generalButton = await driver.findElement(By.xpath(`//div[contains(text(), 'General')]`))
await generalButton.click()
})
it('turns on use of blockies', async () => {
const toggleButton = await findElement(driver, By.css('.toggle-button > div'))
const toggleButton = await driver.findElement(By.css('.toggle-button > div'))
await toggleButton.click()
})
it('adds an address to the contact list', async () => {
const contactsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Contacts')]`))
const contactsButton = await driver.findElement(By.xpath(`//div[contains(text(), 'Contacts')]`))
await contactsButton.click()
const addressBookAddButton = await findElement(driver, By.css('.address-book-add-button__button'))
const addressBookAddButton = await driver.findElement(By.css('.address-book-add-button__button'))
await addressBookAddButton.click()
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
const addAddressInputs = await findElements(driver, By.css('input'))
const addAddressInputs = await driver.findElements(By.css('input'))
await addAddressInputs[0].sendKeys('Test User Name 11')
await delay(tinyDelayMs)
await driver.delay(tinyDelayMs)
await addAddressInputs[1].sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
await delay(largeDelayMs * 2)
await driver.delay(largeDelayMs * 2)
const saveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
const saveButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Save')]`))
await saveButton.click()
await findElement(driver, By.xpath(`//div[contains(text(), 'Test User Name 11')]`))
await delay(regularDelayMs)
await driver.findElement(By.xpath(`//div[contains(text(), 'Test User Name 11')]`))
await driver.delay(regularDelayMs)
})
})
@ -179,7 +173,6 @@ describe('MetaMask', function () {
before(async function () {
const result = await prepareExtensionForTesting({ port: await getPort() })
driver2 = result.driver
await setupFetchMocking(driver2)
})
after(async function () {
@ -188,84 +181,86 @@ describe('MetaMask', function () {
describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver2, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver2, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver2.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver2.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver2.delay(largeDelayMs)
})
it('clicks the "Import Wallet" option', async () => {
const customRpcButton = await findElement(driver2, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
const customRpcButton = await driver2.findElement(By.xpath(`//button[contains(text(), 'Import Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver2.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver2, By.css('.btn-default'))
const optOutButton = await driver2.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver2.delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver2, By.css('textarea.first-time-flow__textarea'))
const [seedTextArea] = await driver2.findElements(By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver2.delay(regularDelayMs)
const [password] = await findElements(driver2, By.id('password'))
const [password] = await driver2.findElements(By.id('password'))
await password.sendKeys('correct horse battery staple')
const [confirmPassword] = await findElements(driver2, By.id('confirm-password'))
const [confirmPassword] = await driver2.findElements(By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver2, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver2.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver2, By.xpath(`//button[contains(text(), 'Import')]`))
const [importButton] = await driver2.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click()
await delay(regularDelayMs)
await driver2.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver2, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver2, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver2.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver2.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver2.delay(regularDelayMs)
})
it('balance renders', async () => {
const balance = await findElement(driver2, By.css('.balance-display .token-amount'))
const balance = await driver2.findElement(By.css('.balance-display .token-amount'))
await driver2.wait(until.elementTextMatches(balance, /25\s*ETH/))
await delay(regularDelayMs)
await driver2.delay(regularDelayMs)
})
})
describe('restores 3box data', () => {
it('confirms the 3box restore notification', async () => {
const restoreButton = await findElement(driver2, By.css('.home-notification__accept-button'))
const restoreButton = await driver2.findElement(By.css('.home-notification__accept-button'))
await restoreButton.click()
})
// TODO: Fix tests from here forward; they're using the wrong driver
it('goes to the settings screen', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
const accountMenuButton = await driver.findElement(By.css('.account-menu__icon'))
await accountMenuButton.click()
await driver.delay(regularDelayMs)
const settingsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Settings')]`))
const settingsButton = await driver.findElement(By.xpath(`//div[contains(text(), 'Settings')]`))
settingsButton.click()
})
it('finds the blockies toggle turned on', async () => {
await delay(regularDelayMs)
const toggleLabel = await findElement(driver, By.css('.toggle-button__status-label'))
await driver.delay(regularDelayMs)
const toggleLabel = await driver.findElement(By.css('.toggle-button__status-label'))
const toggleLabelText = await toggleLabel.getText()
assert.equal(toggleLabelText, 'ON')
})
it('finds the restored address in the contact list', async () => {
const contactsButton = await findElement(driver, By.xpath(`//div[contains(text(), 'Contacts')]`))
const contactsButton = await driver.findElement(By.xpath(`//div[contains(text(), 'Contacts')]`))
await contactsButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await findElement(driver, By.xpath(`//div[contains(text(), 'Test User Name 11')]`))
await delay(regularDelayMs)
await driver.findElement(By.xpath(`//div[contains(text(), 'Test User Name 11')]`))
await driver.delay(regularDelayMs)
})
})
})

@ -3,16 +3,9 @@ const webdriver = require('selenium-webdriver')
const { By } = webdriver
const {
checkBrowserForConsoleErrors,
delay,
findElement,
findElements,
openNewPage,
switchToWindowWithTitle,
verboseReportOnFailure,
waitUntilXWindowHandles,
setupFetchMocking,
prepareExtensionForTesting,
regularDelayMs,
largeDelayMs,
} = require('./helpers')
const enLocaleMessages = require('../../app/_locales/en/messages.json')
@ -20,14 +13,12 @@ describe('Using MetaMask with an existing account', function () {
let driver
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
const button = async (x) => {
const buttoncheck = x
await buttoncheck.click()
await delay(largeDelayMs)
const [results] = await findElements(driver, By.css('#results'))
await driver.delay(largeDelayMs)
const [results] = await driver.findElements(By.css('#results'))
const resulttext = await results.getText()
const parsedData = JSON.parse(resulttext)
@ -41,12 +32,11 @@ describe('Using MetaMask with an existing account', function () {
before(async function () {
const result = await prepareExtensionForTesting()
driver = result.driver
await setupFetchMocking(driver)
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
const errors = await driver.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
@ -54,7 +44,7 @@ describe('Using MetaMask with an existing account', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
await driver.verboseReportOnFailure(driver, this.currentTest)
}
})
@ -64,47 +54,47 @@ describe('Using MetaMask with an existing account', function () {
describe('First time flow starting from an existing seed phrase', () => {
it('clicks the continue button on the welcome screen', async () => {
await findElement(driver, By.css('.welcome-page__header'))
const welcomeScreenBtn = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
await driver.findElement(By.css('.welcome-page__header'))
const welcomeScreenBtn = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
welcomeScreenBtn.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "Import Wallet" option', async () => {
const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`))
const customRpcButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Import Wallet')]`))
customRpcButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
const optOutButton = await driver.findElement(By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
await driver.delay(largeDelayMs)
})
it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
const [seedTextArea] = await driver.findElements(By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [password] = await findElements(driver, By.id('password'))
const [password] = await driver.findElements(By.id('password'))
await password.sendKeys('correct horse battery staple')
const [confirmPassword] = await findElements(driver, By.id('confirm-password'))
const [confirmPassword] = await driver.findElements(By.id('confirm-password'))
confirmPassword.sendKeys('correct horse battery staple')
const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox'))
const tosCheckBox = await driver.findElement(By.css('.first-time-flow__checkbox'))
await tosCheckBox.click()
const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
const [importButton] = await driver.findElements(By.xpath(`//button[contains(text(), 'Import')]`))
await importButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await driver.findElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
await doneButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
})
})
@ -112,37 +102,37 @@ describe('Using MetaMask with an existing account', function () {
describe('opens dapp', () => {
it('switches to mainnet', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
const networkDropdown = await driver.findElement(By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
const [mainnet] = await findElements(driver, By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`))
const [mainnet] = await driver.findElements(By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`))
await mainnet.click()
await delay(largeDelayMs * 2)
await driver.delay(largeDelayMs * 2)
})
it('connects to dapp', async () => {
await openNewPage(driver, 'http://127.0.0.1:8080/')
await delay(regularDelayMs)
await driver.openNewPage('http://127.0.0.1:8080/')
await driver.delay(regularDelayMs)
const connectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
const connectButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Connect')]`))
await connectButton.click()
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
await waitUntilXWindowHandles(driver, 3)
await driver.waitUntilXWindowHandles(3)
const windowHandles = await driver.getAllWindowHandles()
const extension = windowHandles[0]
const popup = await switchToWindowWithTitle(driver, 'MetaMask Notification', windowHandles)
const popup = await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles)
const dapp = windowHandles.find(handle => handle !== extension && handle !== popup)
await delay(regularDelayMs)
const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`))
await driver.delay(regularDelayMs)
const approveButton = await driver.findElement(By.xpath(`//button[contains(text(), 'Connect')]`))
await approveButton.click()
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
await driver.switchToWindow(dapp)
await driver.delay(regularDelayMs)
})
})
@ -162,7 +152,7 @@ describe('Using MetaMask with an existing account', function () {
const result = parseInt(parsedData.result, 16)
assert.equal((typeof result === 'number'), true)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
} catch (err) {
console.log(err)
assert(false)
@ -183,7 +173,7 @@ describe('Using MetaMask with an existing account', function () {
const result = parsedData.result
assert.equal(result, false)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
} catch (err) {
console.log(err)
assert(false)
@ -251,7 +241,7 @@ describe('Using MetaMask with an existing account', function () {
const result = parseInt(parsedData.result.parentHash, 16)
assert.equal((typeof result === 'number'), true)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
} catch (err) {
console.log(err)
@ -279,7 +269,7 @@ describe('Using MetaMask with an existing account', function () {
result = parseInt(parsedData.result.blockHash, 16)
assert.equal((typeof result === 'number' || (result === 0)), true)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
} else {
parsedData = await button(List[i])
console.log(parsedData.result)
@ -287,7 +277,7 @@ describe('Using MetaMask with an existing account', function () {
result = parseInt(parsedData.result, 16)
assert.equal((typeof result === 'number' || (result === 0)), true)
await delay(regularDelayMs)
await driver.delay(regularDelayMs)
}

@ -0,0 +1,153 @@
const { promises: fs } = require('fs')
const { until, error: webdriverError } = require('selenium-webdriver')
const { strict: assert } = require('assert')
class Driver {
/**
* @param {!ThenableWebDriver} driver - A {@code WebDriver} instance
* @param {string} browser - The type of browser this driver is controlling
* @param {number} timeout
*/
constructor (driver, browser, timeout = 10000) {
this.driver = driver
this.browser = browser
this.timeout = timeout
}
async delay (time) {
await new Promise(resolve => setTimeout(resolve, time))
}
async wait (condition, timeout = this.timeout) {
await this.driver.wait(condition, timeout)
}
async quit () {
await this.driver.quit()
}
// Element interactions
async findElement (locator) {
return await this.driver.wait(until.elementLocated(locator), this.timeout)
}
async findVisibleElement (locator) {
const element = await this.findElement(locator)
await this.driver.wait(until.elementIsVisible(element), this.timeout)
return element
}
findElements (locator) {
return this.driver.wait(until.elementsLocated(locator), this.timeout)
}
async clickElement (locator) {
const element = await this.findElement(locator)
await element.click()
}
async scrollToElement (element) {
await this.driver.executeScript('arguments[0].scrollIntoView(true)', element)
}
async assertElementNotPresent (locator) {
let dataTab
try {
dataTab = await this.findElement(locator)
} catch (err) {
assert(err instanceof webdriverError.NoSuchElementError || err instanceof webdriverError.TimeoutError)
}
assert.ok(!dataTab, 'Found element that should not be present')
}
// Window management
async openNewPage (url) {
const newHandle = await this.driver.switchTo().newWindow()
await this.driver.get(url)
return newHandle
}
async switchToWindow (handle) {
await this.driver.switchTo().window(handle)
}
async getAllWindowHandles () {
return await this.driver.getAllWindowHandles()
}
async waitUntilXWindowHandles (x, delayStep = 1000, timeout = 5000) {
let timeElapsed = 0
while (timeElapsed <= timeout) {
const windowHandles = await this.driver.getAllWindowHandles()
if (windowHandles.length === x) {
return
}
await this.delay(delayStep)
timeElapsed += delayStep
}
throw new Error('waitUntilXWindowHandles timed out polling window handles')
}
async switchToWindowWithTitle (title, windowHandles) {
if (!windowHandles) {
windowHandles = await this.driver.getAllWindowHandles()
}
for (const handle of windowHandles) {
await this.driver.switchTo().window(handle)
const handleTitle = await this.driver.getTitle()
if (handleTitle === title) {
return handle
}
}
throw new Error('No window with title: ' + title)
}
/**
* Closes all windows except those in the given list of exceptions
* @param {Array<string>} exceptions the list of window handle exceptions
* @param {Array?} windowHandles the full list of window handles
* @returns {Promise<void>}
*/
async closeAllWindowHandlesExcept (exceptions, windowHandles) {
windowHandles = windowHandles || await this.driver.getAllWindowHandles()
for (const handle of windowHandles) {
if (!exceptions.includes(handle)) {
await this.driver.switchTo().window(handle)
await this.delay(1000)
await this.driver.close()
await this.delay(1000)
}
}
}
// Error handling
async verboseReportOnFailure (test) {
const artifactDir = `./test-artifacts/${this.browser}/${test.title}`
const filepathBase = `${artifactDir}/test-failure`
await fs.mkdir(artifactDir, { recursive: true })
const screenshot = await this.driver.takeScreenshot()
await fs.writeFile(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
const htmlSource = await this.driver.getPageSource()
await fs.writeFile(`${filepathBase}-dom.html`, htmlSource)
}
async checkBrowserForConsoleErrors () {
const ignoredLogTypes = ['WARNING']
const ignoredErrorMessages = [
// Third-party Favicon 404s show up as errors
'favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)',
]
const browserLogs = await this.driver.manage().logs().get('browser')
const errorEntries = browserLogs.filter(entry => !ignoredLogTypes.includes(entry.level.toString()))
const errorObjects = errorEntries.map(entry => entry.toJSON())
return errorObjects.filter(entry => !ignoredErrorMessages.some(message => entry.message.includes(message)))
}
}
module.exports = Driver

@ -1,23 +1,29 @@
const { Browser } = require('selenium-webdriver')
const Driver = require('./driver')
const ChromeDriver = require('./chrome')
const FirefoxDriver = require('./firefox')
const fetchMockResponses = require('../../data/fetch-mocks.json')
const buildWebDriver = async function buildWebDriver ({ browser, extensionPath, responsive, port }) {
async function buildWebDriver ({ browser, extensionPath, responsive, port }) {
switch (browser) {
case Browser.CHROME: {
const { driver, extensionId, extensionUrl } = await ChromeDriver.build({ extensionPath, responsive, port })
setupFetchMocking(driver)
await driver.get(extensionUrl)
return {
driver,
driver: new Driver(driver, browser),
extensionId,
extensionUrl,
}
}
case Browser.FIREFOX: {
const { driver, extensionId, extensionUrl } = await FirefoxDriver.build({ extensionPath, responsive, port })
setupFetchMocking(driver)
await driver.get(extensionUrl)
return {
driver,
driver: new Driver(driver, browser),
extensionId,
extensionUrl,
}
@ -28,6 +34,38 @@ const buildWebDriver = async function buildWebDriver ({ browser, extensionPath,
}
}
async function setupFetchMocking (driver) {
// define fetchMocking script, to be evaluated in the browser
function fetchMocking (fetchMockResponses) {
window.origFetch = window.fetch.bind(window)
window.fetch = async (...args) => {
const url = args[0]
if (url === 'https://ethgasstation.info/json/ethgasAPI.json') {
return { json: async () => clone(fetchMockResponses.ethGasBasic) }
} else if (url === 'https://ethgasstation.info/json/predictTable.json') {
return { json: async () => clone(fetchMockResponses.ethGasPredictTable) }
} else if (url.match(/chromeextensionmm/)) {
return { json: async () => clone(fetchMockResponses.metametrics) }
}
return window.origFetch(...args)
}
if (window.chrome && window.chrome.webRequest) {
window.chrome.webRequest.onBeforeRequest.addListener(cancelInfuraRequest, { urls: ['https://*.infura.io/*'] }, ['blocking'])
}
function cancelInfuraRequest (requestDetails) {
console.log(`fetchMocking - Canceling request: "${requestDetails.url}"`)
return { cancel: true }
}
function clone (obj) {
return JSON.parse(JSON.stringify(obj))
}
}
// fetchMockResponses are parsed last minute to ensure that objects are uniquely instantiated
const fetchMockResponsesJson = JSON.stringify(fetchMockResponses)
// eval the fetchMocking script in the browser
await driver.executeScript(`(${fetchMocking})(${fetchMockResponsesJson})`)
}
module.exports = {
buildWebDriver,
}

@ -3,7 +3,7 @@ const {
timeout,
queryAsync,
} = require('../../lib/util')
const fetchMockResponses = require('../../e2e/fetch-mocks.json')
const fetchMockResponses = require('../../data/fetch-mocks.json')
QUnit.module('confirm sig requests')

@ -4,7 +4,7 @@ const {
queryAsync,
findAsync,
} = require('../../lib/util')
const fetchMockResponses = require('../../e2e/fetch-mocks.json')
const fetchMockResponses = require('../../data/fetch-mocks.json')
QUnit.module('currency localization')

@ -3,7 +3,7 @@ const {
queryAsync,
findAsync,
} = require('../../lib/util')
const fetchMockResponses = require('../../e2e/fetch-mocks.json')
const fetchMockResponses = require('../../data/fetch-mocks.json')
QUnit.module('tx list items')

Loading…
Cancel
Save