diff --git a/test/e2e/func.js b/test/e2e/func.js index 7b1730959..fff90c463 100644 --- a/test/e2e/func.js +++ b/test/e2e/func.js @@ -5,7 +5,8 @@ const os = require('os') const path = require('path') const webdriver = require('selenium-webdriver') const Command = require('selenium-webdriver/lib/command').Command -const By = webdriver.By + +const { By, Key, until } = webdriver module.exports = { delay, @@ -14,13 +15,14 @@ module.exports = { installWebExt, getExtensionIdChrome, getExtensionIdFirefox, + clearField } -function delay (time) { +function delay(time) { return new Promise(resolve => setTimeout(resolve, time)) } -function buildChromeWebDriver (extPath) { +function buildChromeWebDriver(extPath) { const tmpProfile = fs.mkdtempSync(path.join(os.tmpdir(), 'mm-chrome-profile')) return new webdriver.Builder() .withCapabilities({ @@ -35,23 +37,23 @@ function buildChromeWebDriver (extPath) { .build() } -function buildFirefoxWebdriver () { +function buildFirefoxWebdriver() { return new webdriver.Builder().build() } -async function getExtensionIdChrome (driver) { +async function getExtensionIdChrome(driver) { await driver.get('chrome://extensions') const extensionId = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-view-manager extensions-item-list").shadowRoot.querySelector("extensions-item:nth-child(2)").getAttribute("id")') return extensionId } -async function getExtensionIdFirefox (driver) { +async function getExtensionIdFirefox(driver) { await driver.get('about:debugging#addons') const extensionId = await driver.findElement(By.css('dd.addon-target-info-content:nth-child(6) > span:nth-child(1)')).getText() return extensionId } -async function installWebExt (driver, extension) { +async function installWebExt(driver, extension) { const cmd = await new Command('moz-install-web-ext') .setParameter('path', path.resolve(extension)) .setParameter('temporary', true) @@ -61,3 +63,11 @@ async function installWebExt (driver, extension) { return await driver.schedule(cmd, 'installWebExt(' + extension + ')') } + +async function clearField(field) { + await field.click(); + await delay(200); + for ( let i = 0; i < 40; i++ ) { + await field.sendKeys(Key.BACK_SPACE); + } +} \ No newline at end of file diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js index d615e42e6..37819fdcc 100644 --- a/test/e2e/metamask.spec.js +++ b/test/e2e/metamask.spec.js @@ -5,27 +5,67 @@ const assert = require('assert') const pify = require('pify') const webdriver = require('selenium-webdriver') const { By, Key, until } = webdriver -const { delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('./func') +const { clearField, delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('./func') const accountsMenuSelector = '#app-content > div > div.full-width > div > div:nth-child(2) > span > div' const settingsTitleSelector = '#app-content > div > div.app-primary.from-right > div > div.section-title.flex-row.flex-center > h2' const deleteImportedAccountTitleSelector = '#app-content > div > div.app-primary.from-left > div > div.section-title.flex-row.flex-center > h2' const importedAccountRemoveIconSelector = '#app-content > div > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(4) > div.remove' const importedLabelSelector = '#app-content > div > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(4) > div.keyring-label' +const buttonChangePassword = '#app-content > div > div.app-primary.from-right > div > div.flex-column.flex-justify-center.flex-grow.select-none > div > div:nth-child(10) > button:nth-child(5)' +let password = '123456789' -describe('Metamask popup page', function () { +const sandwichMenuSelectors = { + Menu: '.sandwich-expando', + Settings: '#app-content > div > div:nth-child(3) > span > div > li:nth-child(2)', + LogOut: '#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)' +} + +const titlesOfScreensSelectors = { + ChangePassword: 'Change Password', + Settings: 'Settings' +} +const mainScreenSelectors = { + buttonBuy: '#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(3)' +} + +const screenChangePassword = { + ById: { + fieldOldPassword: 'old-password-box', + fieldNewPassword: 'new-password-box', + fieldConfirmNewPassword: 'password-box-confirm' + }, + ByCss: { + buttonNo: '#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-right > button.btn-violet', + buttonYes: '#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-right > button:nth-child(2)', + }, + ByClassName: { + label: 'confirm-label', + arrowLeft: 'fa fa-arrow-left fa-lg cursor-pointer', + error: 'error' + }, + labelText: 'Are you sure you want to change the password for unlocking of your wallet?', + error: { + differ: 'New password should differ from the current one', + notLong: 'Password not long enough', + dontMatch: 'Passwords don\'t match', + incorrectPassword: 'Incorrect password' + } +} + +describe('Metamask popup page', async function () { let driver, accountAddress, tokenAddress, extensionId this.timeout(0) before(async function () { - if (process.env.SELENIUM_BROWSER === 'chrome') { + if ( process.env.SELENIUM_BROWSER === 'chrome' ) { const extPath = path.resolve('dist/chrome') driver = buildChromeWebDriver(extPath) extensionId = await getExtensionIdChrome(driver) await driver.get(`chrome-extension://${extensionId}/popup.html`) - } else if (process.env.SELENIUM_BROWSER === 'firefox') { + } else if ( process.env.SELENIUM_BROWSER === 'firefox' ) { const extPath = path.resolve('dist/firefox') driver = buildFirefoxWebdriver() await installWebExt(driver, extPath) @@ -38,17 +78,17 @@ describe('Metamask popup page', function () { afterEach(async function () { // logs command not supported in firefox // https://github.com/SeleniumHQ/selenium/issues/2910 - if (process.env.SELENIUM_BROWSER === 'chrome') { + if ( process.env.SELENIUM_BROWSER === 'chrome' ) { // check for console errors const errors = await checkBrowserForConsoleErrors() - if (errors.length) { + if ( errors.length ) { const errorReports = errors.map(err => err.message) const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}` this.test.error(new Error(errorMessage)) } } // gather extra data if test failed - if (this.currentTest.state === 'failed') { + if ( this.currentTest.state === 'failed' ) { await verboseReportOnFailure(this.currentTest) } }) @@ -57,7 +97,7 @@ describe('Metamask popup page', function () { await driver.quit() }) - describe('Setup', function () { + describe('Setup', async function () { it('switches to Chrome extensions list', async function () { await delay(300) @@ -72,7 +112,7 @@ describe('Metamask popup page', function () { }) - describe('Account Creation', () => { + describe('Account Creation', async () => { it('matches Nifty Wallet title', async () => { const title = await driver.getTitle() @@ -104,8 +144,8 @@ describe('Metamask popup page', function () { const passwordBoxConfirm = await driver.findElement(By.id('password-box-confirm')) const button = await driver.findElements(By.css('button')) - await passwordBox.sendKeys('123456789') - await passwordBoxConfirm.sendKeys('123456789') + await passwordBox.sendKeys(password) + await passwordBoxConfirm.sendKeys(password) await button[0].click() await delay(500) }) @@ -142,7 +182,7 @@ describe('Metamask popup page', function () { it('accepts account password after lock', async () => { await delay(500) - await driver.findElement(By.id('password-box')).sendKeys('123456789') + await driver.findElement(By.id('password-box')).sendKeys(password) await driver.findElement(By.id('password-box')).sendKeys(Key.ENTER) await delay(500) }) @@ -162,8 +202,150 @@ describe('Metamask popup page', function () { }) }) + describe('Change password', async () => { + const newPassword = { + correct: 'abcDEF123!@#', + short: '123', + incorrect: '1234567890' + } + let fieldNewPassword + let fieldConfirmNewPassword + let fieldOldPassword + let buttonYes + + describe('check screen "Settings" -> "Change password" ', async () => { + + it('checks if "Change password" button is present and enabled', async () => { + await driver.findElement(By.css(sandwichMenuSelectors.Menu)).click() + await delay(500) + await driver.findElement(By.css(sandwichMenuSelectors.Settings)).click() + await delay(500) + const buttons = await driver.findElements(By.css(buttonChangePassword)) + assert.equal(buttons.length, 1, 'Button "Change password" is not present') + assert.equal(await buttons[0].isEnabled(), true, 'Button "Change password" is disabled') + }) + + it('screen contains correct title', async () => { + const button = await driver.findElement(By.css(buttonChangePassword)) + await button.click() + const title = await driver.findElement(By.className('page-subtitle')) + assert.equal(await title.getText(), titlesOfScreensSelectors.ChangePassword, '"Change password" screen contains incorrect title') + }) + + it('screen contains correct label', async () => { + const labels = await driver.findElements(By.className(screenChangePassword.ByClassName.label)) + assert.equal(labels.length, 1, 'screen "Change password" doesn\'t contain label') + assert.equal(await labels[0].getText(), screenChangePassword.labelText, 'label contains incorrect title') + }) + + it('clicking the button "No" bring back to "Setting" screen ', async () => { + const button = await driver.findElement(By.css(screenChangePassword.ByCss.buttonNo)) + await button.click() + const title = await driver.findElement(By.css(settingsTitleSelector)) + assert.equal(await title.getText(), titlesOfScreensSelectors.Settings, 'button "No" doesnt open settings screen') + const buttonChangePass = await driver.findElement(By.css(buttonChangePassword)) + await buttonChangePass.click() + }) + }) + + describe('Validation of errors ', async () => { + + before(async () => { + fieldOldPassword = await driver.findElement(By.id(screenChangePassword.ById.fieldOldPassword)) + await fieldOldPassword.sendKeys(password) + fieldNewPassword = await driver.findElement(By.id(screenChangePassword.ById.fieldNewPassword)) + fieldConfirmNewPassword = await driver.findElement(By.id(screenChangePassword.ById.fieldConfirmNewPassword)) + buttonYes = await driver.findElement(By.css(screenChangePassword.ByCss.buttonYes)) + }) + + it('error if new password shorter than 8 digits', async () => { + await fieldNewPassword.sendKeys(newPassword.short) + await fieldConfirmNewPassword.sendKeys(newPassword.short) + await buttonYes.click() + const errors = await driver.findElements(By.className(screenChangePassword.ByClassName.error)) + assert.equal(errors.length > 0, true, 'error isn\'t displayed') + assert.equal(await errors[0].getText(), screenChangePassword.error.notLong, 'Error\'s text incorrect') + }) + + it('error if new password doesn\'t match confirmation', async () => { + await clearField(fieldNewPassword) + await clearField(fieldConfirmNewPassword) + await fieldNewPassword.sendKeys(newPassword.correct) + await fieldConfirmNewPassword.sendKeys(newPassword.incorrect) + await buttonYes.click() + const errors = await driver.findElements(By.className(screenChangePassword.ByClassName.error)) + assert.equal(errors.length > 0, true, 'error isn\'t displayed') + assert.equal(await errors[0].getText(), screenChangePassword.error.dontMatch, 'Error\'s text incorrect') + }) + + it('error if new password match old password', async () => { + await clearField(fieldNewPassword) + await clearField(fieldConfirmNewPassword) + await fieldNewPassword.sendKeys(password) + await fieldConfirmNewPassword.sendKeys(password) + await buttonYes.click() + const errors = await driver.findElements(By.className(screenChangePassword.ByClassName.error)) + assert.equal(errors.length > 0, true, 'error isn\'t displayed') + assert.equal(await errors[0].getText(), screenChangePassword.error.differ, 'Error\'s text incorrect') + }) + + it.skip('error if old password incorrect ', async () => { + await clearField(fieldOldPassword) + await fieldOldPassword.sendKeys(newPassword.incorrect) + await buttonYes.click() + const errors = await driver.findElements(By.className(screenChangePassword.ByClassName.error)) + assert.equal(errors.length > 0, true, 'error isn\'t displayed') + assert.equal(await errors[0].getText(), screenChangePassword.error.incorrectPassword, 'Error\'s text incorrect') + }) + + it('no errors if old, new, confirm new passwords are correct; user can change password', async () => { + await clearField(fieldNewPassword) + await clearField(fieldOldPassword) + await clearField(fieldConfirmNewPassword) + + await fieldOldPassword.sendKeys(password) + await fieldNewPassword.sendKeys(newPassword.correct) + await fieldConfirmNewPassword.sendKeys(newPassword.correct) + await buttonYes.click() + + await driver.wait(until.elementLocated(By.css(buttonChangePassword))) + const buttons = await driver.findElements(By.css(buttonChangePassword)) + assert.equal(buttons.length, 1, 'Button "Change password" is not present') + assert.equal(await buttons[0].isEnabled(), true, 'Button "Change password" is disabled') + }) + }) + + describe('Check if new password is accepted', async () => { + + it('user can log out', async () => { + await driver.findElement(By.css(sandwichMenuSelectors.Menu)).click() + await delay(500) + await driver.wait(until.elementLocated(By.css(sandwichMenuSelectors.LogOut))) + const itemLogOut = await driver.findElement(By.css(sandwichMenuSelectors.LogOut)) + await driver.wait(until.elementIsVisible(itemLogOut)) + itemLogOut.click() + await driver.wait(until.elementLocated(By.id('password-box'))) + const fields = await driver.findElements(By.id('password-box')) + assert.equal(fields.length, 1, 'password box isn\'t present after logout') + }) + + it('accepts new password after lock', async () => { + const field = await driver.findElement(By.id('password-box')) + await field.sendKeys(newPassword.correct) + await driver.findElement(By.className('cursor-pointer')).click() + + await driver.wait(until.elementLocated(By.css(mainScreenSelectors.buttonBuy))) + const buttons = await driver.findElements(By.css(mainScreenSelectors.buttonBuy)) + assert.equal(buttons.length, 1, 'main screen isn\'t displayed') + password = newPassword.correct + }) + }) + }) + describe('Import Account', () => { + it('opens import account menu', async function () { + await driver.wait(until.elementLocated(By.css(accountsMenuSelector))) await driver.findElement(By.css(accountsMenuSelector)).click() await delay(500) await driver.findElement(By.css('#app-content > div > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(5) > span')).click() @@ -265,10 +447,10 @@ describe('Metamask popup page', function () { }) it('sends transaction', async function () { - const sendButton = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(4)')) - assert.equal(await sendButton.getText(), 'Send') - await sendButton.click() - await delay(200) + const sendButton = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(4)')) + assert.equal(await sendButton.getText(), 'Send') + await sendButton.click() + await delay(200) }) it('adds recipient address and amount', async function () { @@ -343,9 +525,9 @@ describe('Metamask popup page', function () { }) it('navigates back to MetaMask popup in the tab', async function () { - if (process.env.SELENIUM_BROWSER === 'chrome') { + if ( process.env.SELENIUM_BROWSER === 'chrome' ) { await driver.get(`chrome-extension://${extensionId}/popup.html`) - } else if (process.env.SELENIUM_BROWSER === 'firefox') { + } else if ( process.env.SELENIUM_BROWSER === 'firefox' ) { await driver.get(`moz-extension://${extensionId}/popup.html`) } await delay(700) @@ -441,7 +623,7 @@ describe('Metamask popup page', function () { const input = await driver.findElement(By.css('#new_rpc')) input.sendKeys(customUrl) await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div.flex-column.flex-justify-center.flex-grow.select-none > div > div:nth-child(2) > button')).click() - if (process.env.SELENIUM_BROWSER === 'firefox') { + if ( process.env.SELENIUM_BROWSER === 'firefox' ) { input.sendKeys(Key.ENTER) } await delay(400) @@ -463,11 +645,11 @@ describe('Metamask popup page', function () { }) }) - async function setProviderType (type) { + async function setProviderType(type) { await driver.executeScript('window.metamask.setProviderType(arguments[0])', type) } - async function checkBrowserForConsoleErrors () { + async function checkBrowserForConsoleErrors() { const ignoredLogTypes = ['WARNING'] const ignoredErrorMessages = [ // React throws error warnings on "dataset", but still sets the data-* properties correctly @@ -487,11 +669,11 @@ describe('Metamask popup page', function () { return matchedErrorObjects } - async function verboseReportOnFailure (test) { + async function verboseReportOnFailure(test) { let artifactDir - if (process.env.SELENIUM_BROWSER === 'chrome') { + if ( process.env.SELENIUM_BROWSER === 'chrome' ) { artifactDir = `./test-artifacts/chrome/${test.title}` - } else if (process.env.SELENIUM_BROWSER === 'firefox') { + } else if ( process.env.SELENIUM_BROWSER === 'firefox' ) { artifactDir = `./test-artifacts/firefox/${test.title}` } const filepathBase = `${artifactDir}/test-failure` @@ -505,3 +687,5 @@ describe('Metamask popup page', function () { } }) + +