From 57be0e49d151ddf29dd5ce856914042748f46d31 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Tue, 13 Apr 2021 16:36:02 -0500 Subject: [PATCH] use waitForSelector instead of until (#10852) * complete abstraction of until method * response to feedback --- test/e2e/from-import-ui.spec.js | 15 +++--- test/e2e/incremental-security.spec.js | 37 +++++++------ test/e2e/metamask-responsive-ui.spec.js | 25 +++++---- test/e2e/metamask-ui.spec.js | 72 +++++++++++-------------- test/e2e/tests/address-book.spec.js | 32 +++++------ test/e2e/tests/send-edit.spec.js | 7 +-- test/e2e/webdriver/driver.js | 24 ++++++--- 7 files changed, 112 insertions(+), 100 deletions(-) diff --git a/test/e2e/from-import-ui.spec.js b/test/e2e/from-import-ui.spec.js index 4a1dfe15e..286c00294 100644 --- a/test/e2e/from-import-ui.spec.js +++ b/test/e2e/from-import-ui.spec.js @@ -1,6 +1,4 @@ const assert = require('assert'); -const { until } = require('selenium-webdriver'); - const enLocaleMessages = require('../../app/_locales/en/messages.json'); const { regularDelayMs, largeDelayMs } = require('./helpers'); const { buildWebDriver } = require('./webdriver'); @@ -126,11 +124,13 @@ describe('Using MetaMask with an existing account', function () { '[data-testid="account-options-menu__account-details"]', ); await driver.findVisibleElement('.qr-code__wrapper'); - const detailModal = await driver.findElement('span .modal'); + // wait for details modal to be visible + const detailsModal = await driver.findVisibleElement('span .modal'); await driver.delay(regularDelayMs); await driver.clickElement('.account-modal__close'); - await driver.wait(until.stalenessOf(detailModal)); + // wait for details modal to be removed from DOM + await detailsModal.waitForElementState('hidden'); await driver.delay(regularDelayMs); }); }); @@ -213,10 +213,11 @@ describe('Using MetaMask with an existing account', function () { await driver.clickElement('.advanced-gas-options-btn'); await driver.delay(regularDelayMs); - const gasModal = await driver.findElement('span .modal'); + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); await driver.clickElement({ text: 'Save', tag: 'button' }); - await driver.wait(until.stalenessOf(gasModal)); - await driver.delay(regularDelayMs); + // wait for gas modal to be removed from DOM + await gasModal.waitForElementState('hidden'); // Continue to next screen await driver.clickElement({ text: 'Next', tag: 'button' }); diff --git a/test/e2e/incremental-security.spec.js b/test/e2e/incremental-security.spec.js index 2e754ec6d..c1b78397c 100644 --- a/test/e2e/incremental-security.spec.js +++ b/test/e2e/incremental-security.spec.js @@ -1,5 +1,4 @@ const assert = require('assert'); -const { until } = require('selenium-webdriver'); const enLocaleMessages = require('../../app/_locales/en/messages.json'); const { tinyDelayMs, regularDelayMs, largeDelayMs } = require('./helpers'); @@ -109,11 +108,13 @@ describe('MetaMask', function () { const addressInput = await driver.findElement('.readonly-input__input'); publicAddress = await addressInput.getAttribute('value'); - const accountModal = await driver.findElement('span .modal'); + // wait for account modal to be visible + const accountModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.account-modal__close'); - await driver.wait(until.stalenessOf(accountModal)); + // wait for account modal to be removed from DOM + await accountModal.waitForElementState('hidden'); await driver.delay(regularDelayMs); }); }); @@ -134,8 +135,10 @@ describe('MetaMask', function () { await driver.delay(regularDelayMs); await driver.clickElement('#send'); - const txStatus = await driver.findElement('#success'); - await driver.wait(until.elementTextMatches(txStatus, /Success/u), 15000); + await driver.waitForSelector( + { css: '#success', text: 'Success' }, + { timeout: 15000 }, + ); }); it('switches back to MetaMask', async function () { @@ -143,13 +146,13 @@ describe('MetaMask', function () { }); it('should have the correct amount of eth', async function () { - const balances = await driver.findElements( - '.currency-display-component__text', - ); - await driver.wait(until.elementTextMatches(balances[0], /1/u), 15000); - const balance = await balances[0].getText(); + const currencyDisplay = await driver.waitForSelector({ + css: '.currency-display-component__text', + text: '1', + }); + const balance = await currencyDisplay.getText(); - assert.equal(balance, '1'); + assert.strictEqual(balance, '1'); }); }); @@ -213,13 +216,13 @@ describe('MetaMask', function () { }); it('should have the correct amount of eth', async function () { - const balances = await driver.findElements( - '.currency-display-component__text', - ); - await driver.wait(until.elementTextMatches(balances[0], /1/u), 15000); - const balance = await balances[0].getText(); + const currencyDisplay = await driver.waitForSelector({ + css: '.currency-display-component__text', + text: '1', + }); + const balance = await currencyDisplay.getText(); - assert.equal(balance, '1'); + assert.strictEqual(balance, '1'); }); it('should not show a backup reminder', async function () { diff --git a/test/e2e/metamask-responsive-ui.spec.js b/test/e2e/metamask-responsive-ui.spec.js index 2d6e76551..2bb2beca1 100644 --- a/test/e2e/metamask-responsive-ui.spec.js +++ b/test/e2e/metamask-responsive-ui.spec.js @@ -1,5 +1,4 @@ const assert = require('assert'); -const { until } = require('selenium-webdriver'); const enLocaleMessages = require('../../app/_locales/en/messages.json'); const { tinyDelayMs, regularDelayMs, largeDelayMs } = require('./helpers'); @@ -194,10 +193,10 @@ describe('MetaMask', function () { }); it('balance renders', async function () { - const balance = await driver.findElement( - '[data-testid="eth-overview__primary-currency"]', - ); - await driver.wait(until.elementTextMatches(balance, /100\s*ETH/u)); + await driver.waitForSelector({ + css: '[data-testid="eth-overview__primary-currency"]', + text: '100 ETH', + }); await driver.delay(regularDelayMs); }); }); @@ -224,10 +223,11 @@ describe('MetaMask', function () { await driver.clickElement('.advanced-gas-options-btn'); await driver.delay(regularDelayMs); - const gasModal = await driver.findElement('span .modal'); - + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.page-container__header-close-text'); - await driver.wait(until.stalenessOf(gasModal), 10000); + // wait for gas modal to be removed from dom + await gasModal.waitForElementState('hidden'); await driver.delay(regularDelayMs); }); @@ -250,10 +250,13 @@ describe('MetaMask', function () { return confirmedTxes.length === 1; }, 10000); - const txValues = await driver.findElement( - '.transaction-list-item__primary-currency', + await driver.waitForSelector( + { + css: '.transaction-list-item__primary-currency', + text: '-1 ETH', + }, + { timeout: 10000 }, ); - await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/u), 10000); }); }); }); diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index ad27da0f2..a08bc7a0f 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -142,11 +142,11 @@ describe('MetaMask', function () { await driver.delay(regularDelayMs); // wait for permission modal to be visible. - await driver.waitForSelector('span .modal'); + const permissionModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.account-modal__close'); - // wait for account modal to be removed from DOM. - await driver.waitForSelector('span .modal', { state: 'detached' }); + // wait for permission modal to be removed from DOM. + await permissionModal.waitForElementState('hidden'); await driver.delay(regularDelayMs); }); }); @@ -377,12 +377,13 @@ describe('MetaMask', function () { await driver.clickElement('.advanced-gas-options-btn'); await driver.delay(regularDelayMs); + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); + await driver.clickElement({ text: 'Save', tag: 'button' }); // Wait for gas modal to be removed from DOM - await driver.waitForSelector('span .modal', { - state: 'detached', - }); + await gasModal.waitForElementState('hidden'); await driver.delay(regularDelayMs); // Continue to next screen @@ -854,8 +855,8 @@ describe('MetaMask', function () { // Set the gas limit await driver.clickElement('.confirm-detail-row__header-text--edit'); - // wait for gas modal to be detached from DOM - await driver.waitForSelector('span .modal'); + // wait for gas modal to be visible. + const gasModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.page-container__tab:nth-of-type(2)'); await driver.delay(regularDelayMs); @@ -875,7 +876,7 @@ describe('MetaMask', function () { await driver.clickElement({ text: 'Save', tag: 'button' }); // wait for gas modal to be detached from DOM - await driver.waitForSelector('span .modal', { state: 'detached' }); + await gasModal.waitForElementState('hidden'); await driver.clickElement({ text: 'Confirm', tag: 'button' }); await driver.delay(regularDelayMs); @@ -1044,12 +1045,12 @@ describe('MetaMask', function () { it('opens customize gas modal and saves options to continue', async function () { await driver.clickElement('.advanced-gas-options-btn'); - // Wait for gas modal to be visible - await driver.waitForSelector('span .modal'); + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); await driver.findElement('.page-container__title'); await driver.clickElement({ text: 'Save', tag: 'button' }); // wait for gas modal to be removed from DOM. - await driver.waitForSelector('span .modal', { state: 'detached' }); + await gasModal.waitForElementState('hidden'); }); it('transitions to the confirm screen', async function () { @@ -1152,16 +1153,14 @@ describe('MetaMask', function () { ); const transactionAmount = transactionAmounts[0]; assert(await transactionAmount.getText(), '1.5 TST'); - - // Set the gas limit - await driver.clickElement('.confirm-detail-row__header-text--edit'); - await driver.delay(regularDelayMs); - - // wait for gas modal to be visible - await driver.waitForSelector('span .modal'); }); it('customizes gas', async function () { + // Set the gas limit + await driver.clickElement('.confirm-detail-row__header-text--edit'); + await driver.delay(regularDelayMs); + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.page-container__tab:nth-of-type(2)'); await driver.delay(regularDelayMs); @@ -1178,10 +1177,8 @@ describe('MetaMask', function () { await driver.clickElement('.page-container__footer-button'); - // Wait for gas modal to be removed from DOM. - await driver.waitForSelector('span .modal', { - state: 'detached', - }); + // wait for gas modal to be removed from DOM. + await gasModal.waitForElementState('hidden'); const gasFeeInputs = await driver.findElements( '.confirm-detail-row__primary', @@ -1286,17 +1283,14 @@ describe('MetaMask', function () { ); }); - it('opens the gas edit modal', async function () { + it('customizes gas', async function () { await driver.clickElement( '.confirm-approve-content__small-blue-text.cursor-pointer', ); await driver.delay(regularDelayMs); - // Wait for the gas modal to be visible - await driver.waitForSelector('span .modal'); - }); - - it('customizes gas', async function () { + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); await driver.clickElement('.page-container__tab:nth-of-type(2)'); await driver.delay(regularDelayMs); @@ -1313,10 +1307,8 @@ describe('MetaMask', function () { await driver.clickElement('.page-container__footer-button'); - // wait for the gas modal to be removed from DOM. - await driver.waitForSelector('span .modal', { - state: 'detached', - }); + // wait for gas modal to be removed from DOM. + await gasModal.waitForElementState('hidden'); const gasFeeInEth = await driver.findElement( '.confirm-approve-content__transaction-details-content__secondary-fee', @@ -1331,7 +1323,7 @@ describe('MetaMask', function () { await editButtons[1].click(); // wait for permission modal to be visible - await driver.waitForSelector('span .modal'); + const permissionModal = await driver.findVisibleElement('span .modal'); const radioButtons = await driver.findClickableElements( '.edit-approval-permission__edit-section__radio-button', ); @@ -1343,7 +1335,7 @@ describe('MetaMask', function () { await driver.clickElement({ text: 'Save', tag: 'button' }); // wait for permission modal to be removed from DOM. - await driver.waitForSelector('span .modal', { state: 'detached' }); + await permissionModal.waitForElementState('hidden'); const permissionInfo = await driver.findElements( '.confirm-approve-content__medium-text', @@ -1521,14 +1513,14 @@ describe('MetaMask', function () { await driver.clickElement('[data-testid="token-options__hide"]'); // wait for confirm hide modal to be visible - await driver.waitForSelector('span .modal'); + const confirmHideModal = await driver.findVisibleElement('span .modal'); await driver.clickElement( '[data-testid="hide-token-confirmation__hide"]', ); // wait for confirm hide modal to be removed from DOM. - await driver.waitForSelector('span .modal', { state: 'detached' }); + await confirmHideModal.waitForElementState('hidden'); }); }); @@ -1649,15 +1641,15 @@ describe('MetaMask', function () { await driver.clickElement('.btn-danger'); await driver.delay(regularDelayMs); - // wait for confirm delete modal to be visible. - await driver.waitForSelector('span .modal'); + // wait for confirm delete modal to be visible + const confirmDeleteModal = await driver.findVisibleElement('span .modal'); await driver.clickElement( '.button.btn-danger.modal-container__footer-button', ); // wait for confirm delete modal to be removed from DOM. - await driver.waitForSelector('span .modal', { state: 'detached' }); + await confirmDeleteModal.waitForElementState('hidden'); const newNetworkListItems = await driver.findElements( '.networks-tab__networks-list-name', diff --git a/test/e2e/tests/address-book.spec.js b/test/e2e/tests/address-book.spec.js index 5b2f46f9f..4f047fe1b 100644 --- a/test/e2e/tests/address-book.spec.js +++ b/test/e2e/tests/address-book.spec.js @@ -1,5 +1,4 @@ const { strict: assert } = require('assert'); -const { until } = require('selenium-webdriver'); const { withFixtures } = require('../helpers'); describe('Address Book', function () { @@ -34,13 +33,16 @@ describe('Address Book', function () { await driver.clickElement('.dialog.send__dialog.dialog--message'); - const addressBookAddModal = await driver.findElement('span .modal'); + // wait for address book modal to be visible + const addressModal = await driver.findElement('span .modal'); + await driver.findElement('.add-to-address-book-modal'); await driver.fill('.add-to-address-book-modal__input', 'Test Name 1'); await driver.clickElement( '.add-to-address-book-modal__footer .btn-primary', ); - await driver.wait(until.stalenessOf(addressBookAddModal)); + // wait for address book modal to be removed from DOM + await addressModal.waitForElementState('hidden'); const inputAmount = await driver.findElement('.unit-input__input'); await inputAmount.fill('1'); @@ -60,12 +62,12 @@ describe('Address Book', function () { return confirmedTxes.length === 1; }, 10000); - const txValues = await driver.findElement( - '.transaction-list-item__primary-currency', - ); - await driver.wait( - until.elementTextMatches(txValues, /-1\s*ETH/u), - 10000, + await driver.waitForSelector( + { + css: '.transaction-list-item__primary-currency', + text: '-1 ETH', + }, + { timeout: 10000 }, ); }, ); @@ -106,12 +108,12 @@ describe('Address Book', function () { return confirmedTxes.length === 1; }, 10000); - const txValues = await driver.findElement( - '.transaction-list-item__primary-currency', - ); - await driver.wait( - until.elementTextMatches(txValues, /-2\s*ETH/u), - 10000, + await driver.waitForSelector( + { + css: '.transaction-list-item__primary-currency', + text: '-2 ETH', + }, + { timeout: 10000 }, ); }, ); diff --git a/test/e2e/tests/send-edit.spec.js b/test/e2e/tests/send-edit.spec.js index 9afe71ca9..c4b8a5ad5 100644 --- a/test/e2e/tests/send-edit.spec.js +++ b/test/e2e/tests/send-edit.spec.js @@ -1,5 +1,4 @@ const { strict: assert } = require('assert'); -const { until } = require('selenium-webdriver'); const { withFixtures, tinyDelayMs, @@ -46,7 +45,8 @@ describe('Editing Confirm Transaction', function () { await driver.clickElement('.advanced-gas-options-btn'); await driver.delay(regularDelayMs); - const gasModal = await driver.findElement('span .modal'); + // wait for gas modal to be visible + const gasModal = await driver.findVisibleElement('span .modal'); const [gasPriceInput, gasLimitInput] = await driver.findElements( '.advanced-gas-inputs__gas-edit-row__input', @@ -59,7 +59,8 @@ describe('Editing Confirm Transaction', function () { await driver.delay(largeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); - await driver.wait(until.stalenessOf(gasModal)); + // Wait for gas modal to be removed from DOM + await gasModal.waitForElementState('hidden'); await driver.clickElement({ text: 'Next', tag: 'button' }); // has correct updated value on the confirm screen the transaction diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 15dc651f6..0188c8257 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -9,13 +9,23 @@ const cssToXPath = require('css-to-xpath'); * @param {Object} element - Selenium Element * @returns {Object} modified Selenium Element */ -function wrapElementWithAPI(element) { +function wrapElementWithAPI(element, driver) { element.press = (key) => element.sendKeys(key); element.fill = async (input) => { // The 'fill' method in playwright replaces existing input await element.clear(); await element.sendKeys(input); }; + element.waitForElementState = async (state, timeout) => { + switch (state) { + case 'hidden': + return await driver.wait(until.stalenessOf(element), timeout); + case 'visible': + return await driver.wait(until.elementIsVisible(element), timeout); + default: + throw new Error(`Provided state: '${state}' is not supported`); + } + }; return element; } @@ -127,7 +137,7 @@ class Driver { timeout, ); } - return wrapElementWithAPI(element); + return wrapElementWithAPI(element, this); } async quit() { @@ -142,14 +152,14 @@ class Driver { until.elementLocated(locator), this.timeout, ); - return wrapElementWithAPI(element); + return wrapElementWithAPI(element, this); } async findVisibleElement(rawLocator) { const locator = this.buildLocator(rawLocator); const element = await this.findElement(locator); await this.driver.wait(until.elementIsVisible(element), this.timeout); - return wrapElementWithAPI(element); + return wrapElementWithAPI(element, this); } async findClickableElement(rawLocator) { @@ -159,7 +169,7 @@ class Driver { this.driver.wait(until.elementIsVisible(element), this.timeout), this.driver.wait(until.elementIsEnabled(element), this.timeout), ]); - return wrapElementWithAPI(element); + return wrapElementWithAPI(element, this); } async findElements(rawLocator) { @@ -168,7 +178,7 @@ class Driver { until.elementsLocated(locator), this.timeout, ); - return elements.map(wrapElementWithAPI); + return elements.map((element) => wrapElementWithAPI(element, this)); } async findClickableElements(rawLocator) { @@ -183,7 +193,7 @@ class Driver { return acc; }, []), ); - return elements.map(wrapElementWithAPI); + return elements.map((element) => wrapElementWithAPI(element, this)); } async clickElement(rawLocator) {