diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/base.conf.js | 2 | ||||
-rw-r--r-- | test/e2e/func.js | 18 | ||||
-rw-r--r-- | test/e2e/metamask.spec.js | 139 | ||||
-rw-r--r-- | test/integration/lib/tx-list-items.js | 61 | ||||
-rw-r--r-- | test/unit/migrations/023-test.js | 99 | ||||
-rw-r--r-- | test/unit/nonce-tracker-test.js | 36 | ||||
-rw-r--r-- | test/unit/tx-state-manager-test.js | 6 |
7 files changed, 358 insertions, 3 deletions
diff --git a/test/base.conf.js b/test/base.conf.js index adb5357e8..e2e9d44ba 100644 --- a/test/base.conf.js +++ b/test/base.conf.js @@ -19,11 +19,13 @@ module.exports = function(config) { 'test/integration/jquery-3.1.0.min.js', { pattern: 'dist/chrome/images/**/*.*', watched: false, included: false, served: true }, { pattern: 'dist/chrome/fonts/**/*.*', watched: false, included: false, served: true }, + { pattern: 'dist/chrome/_locales/**/*.*', watched: false, included: false, served: true }, ], proxies: { '/images/': '/base/dist/chrome/images/', '/fonts/': '/base/dist/chrome/fonts/', + '/_locales/': '/base/dist/chrome/_locales/', }, // test results reporter to use diff --git a/test/e2e/func.js b/test/e2e/func.js new file mode 100644 index 000000000..733225565 --- /dev/null +++ b/test/e2e/func.js @@ -0,0 +1,18 @@ +require('chromedriver') +const webdriver = require('selenium-webdriver') + +exports.delay = function delay (time) { + return new Promise(resolve => setTimeout(resolve, time)) +} + + +exports.buildWebDriver = function buildWebDriver (extPath) { + return new webdriver.Builder() + .withCapabilities({ + chromeOptions: { + args: [`load-extension=${extPath}`], + }, + }) + .forBrowser('chrome') + .build() +} diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js new file mode 100644 index 000000000..d2ffa041e --- /dev/null +++ b/test/e2e/metamask.spec.js @@ -0,0 +1,139 @@ +const fs = require('fs') +const mkdirp = require('mkdirp') +const path = require('path') +const assert = require('assert') +const pify = require('pify') +const webdriver = require('selenium-webdriver') +const By = webdriver.By +const { delay, buildWebDriver } = require('./func') + +describe('Metamask popup page', function () { + let driver + this.seedPhase + this.accountAddress + this.timeout(0) + + before(async function () { + const extPath = path.resolve('dist/chrome') + driver = buildWebDriver(extPath) + await driver.get('chrome://extensions-frame') + const elems = await driver.findElements(By.css('.extension-list-item-wrapper')) + const extensionId = await elems[1].getAttribute('id') + await driver.get(`chrome-extension://${extensionId}/popup.html`) + await delay(500) + }) + + afterEach(async function () { + if (this.currentTest.state === 'failed') { + await verboseReportOnFailure(this.currentTest) + } + }) + + after(async function () { + await driver.quit() + }) + + describe('#onboarding', () => { + it('should open Metamask.io', async function () { + const tabs = await driver.getAllWindowHandles() + await driver.switchTo().window(tabs[0]) + await delay(300) + }) + + it('should match title', async () => { + const title = await driver.getTitle() + assert.equal(title, 'MetaMask', 'title matches MetaMask') + }) + + it('should show privacy notice', async () => { + const privacy = await driver.findElement(By.css('.terms-header')).getText() + assert.equal(privacy, 'PRIVACY NOTICE', 'shows privacy notice') + driver.findElement(By.css('button')).click() + await delay(300) + }) + + it('should show terms of use', async () => { + await delay(300) + const terms = await driver.findElement(By.css('.terms-header')).getText() + assert.equal(terms, 'TERMS OF USE', 'shows terms of use') + await delay(300) + }) + + it('should be unable to continue without scolling throught the terms of use', async () => { + const button = await driver.findElement(By.css('button')).isEnabled() + assert.equal(button, false, 'disabled continue button') + const element = driver.findElement(By.linkText( + 'Attributions' + )) + await driver.executeScript('arguments[0].scrollIntoView(true)', element) + await delay(300) + }) + + it('should be able to continue when scrolled to the bottom of terms of use', async () => { + const button = await driver.findElement(By.css('button')) + const buttonEnabled = await button.isEnabled() + await delay(500) + assert.equal(buttonEnabled, true, 'enabled continue button') + await button.click() + await delay(300) + }) + + it('should accept password with length of eight', async () => { + const passwordBox = await driver.findElement(By.id('password-box')) + const passwordBoxConfirm = await driver.findElement(By.id('password-box-confirm')) + const button = driver.findElement(By.css('button')) + + passwordBox.sendKeys('123456789') + passwordBoxConfirm.sendKeys('123456789') + await delay(500) + await button.click() + }) + + it('should show value was created and seed phrase', async () => { + await delay(700) + this.seedPhase = await driver.findElement(By.css('.twelve-word-phrase')).getText() + const continueAfterSeedPhrase = await driver.findElement(By.css('button')) + await continueAfterSeedPhrase.click() + await delay(300) + }) + + it('should show lock account', async () => { + await driver.findElement(By.css('.sandwich-expando')).click() + await delay(500) + await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)')).click() + }) + + it('should accept account password after lock', async () => { + await delay(500) + await driver.findElement(By.id('password-box')).sendKeys('123456789') + await driver.findElement(By.css('button')).click() + await delay(500) + }) + + it('should show QR code option', async () => { + await delay(300) + await driver.findElement(By.css('.fa-ellipsis-h')).click() + await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div:nth-child(1) > flex-column > div.name-label > div > span > i > div > div > li:nth-child(3)')).click() + await delay(300) + }) + + it('should show the account address', async () => { + this.accountAddress = await driver.findElement(By.css('.ellip-address')).getText() + await driver.findElement(By.css('.fa-arrow-left')).click() + await delay(500) + }) + }) + + async function verboseReportOnFailure(test) { + const artifactDir = `./test-artifacts/${test.title}` + const filepathBase = `${artifactDir}/test-failure` + await pify(mkdirp)(artifactDir) + // capture screenshot + const screenshot = await driver.takeScreenshot() + await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' }) + // capture dom source + const htmlSource = await driver.getPageSource() + await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource) + } + +}) diff --git a/test/integration/lib/tx-list-items.js b/test/integration/lib/tx-list-items.js new file mode 100644 index 000000000..d0056eb94 --- /dev/null +++ b/test/integration/lib/tx-list-items.js @@ -0,0 +1,61 @@ +const reactTriggerChange = require('../../lib/react-trigger-change') +const { + timeout, + queryAsync, + findAsync, +} = require('../../lib/util') + +QUnit.module('tx list items') + +QUnit.test('renders list items successfully', (assert) => { + const done = assert.async() + runTxListItemsTest(assert).then(done).catch((err) => { + assert.notOk(err, `Error was thrown: ${err.stack}`) + done() + }) +}) + +async function runTxListItemsTest(assert, done) { + console.log('*** start runTxListItemsTest') + const selectState = await queryAsync($, 'select') + selectState.val('tx list items') + reactTriggerChange(selectState[0]) + + const metamaskLogo = await queryAsync($, '.left-menu-wrapper') + assert.ok(metamaskLogo[0], 'metamask logo present') + metamaskLogo[0].click() + + const txListItems = await queryAsync($, '.tx-list-item') + assert.equal(txListItems.length, 8, 'all tx list items are rendered') + + const unapprovedTx = txListItems[0] + assert.equal($(unapprovedTx).hasClass('tx-list-pending-item-container'), true, 'unapprovedTx has the correct class') + + const retryTx = txListItems[1] + const retryTxLink = await findAsync($(retryTx), '.tx-list-item-retry-link') + assert.equal(retryTxLink[0].textContent, 'Increase the gas price on your transaction', 'retryTx has expected link') + + const approvedTx = txListItems[2] + const approvedTxRenderedStatus = await findAsync($(approvedTx), '.tx-list-status') + assert.equal(approvedTxRenderedStatus[0].textContent, 'Approved', 'approvedTx has correct label') + + const unapprovedMsg = txListItems[3] + const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.tx-list-account') + assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description') + + const failedTx = txListItems[4] + const failedTxRenderedStatus = await findAsync($(failedTx), '.tx-list-status') + assert.equal(failedTxRenderedStatus[0].textContent, 'Failed', 'failedTx has correct label') + + const shapeShiftTx = txListItems[5] + const shapeShiftTxStatus = await findAsync($(shapeShiftTx), '.flex-column div:eq(1)') + assert.equal(shapeShiftTxStatus[0].textContent, 'No deposits received', 'shapeShiftTx has correct status') + + const confirmedTokenTx = txListItems[6] + const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.tx-list-account') + assert.equal(confirmedTokenTxAddress[0].textContent, '0xe7884118...81a9', 'confirmedTokenTx has correct address') + + const rejectedTx = txListItems[7] + const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status') + assert.equal(rejectedTxRenderedStatus[0].textContent, 'Rejected', 'rejectedTx has correct label') +} diff --git a/test/unit/migrations/023-test.js b/test/unit/migrations/023-test.js new file mode 100644 index 000000000..be432d9fa --- /dev/null +++ b/test/unit/migrations/023-test.js @@ -0,0 +1,99 @@ +const assert = require('assert') +const migration23 = require('../../../app/scripts/migrations/023') +const properTime = (new Date()).getTime() +const storage = { + "meta": {}, + "data": { + "TransactionController": { + "transactions": [ + ] + }, + }, +} + +const transactions = [] +const transactions40 = [] +const transactions20 = [] + +const txStates = [ + 'unapproved', + 'approved', + 'signed', + 'submitted', + 'confirmed', + 'rejected', + 'failed', + 'dropped', +] + +const deletableTxStates = [ + 'confirmed', + 'rejected', + 'failed', + 'dropped', +] + +let nonDeletableCount = 0 + +let status +while (transactions.length <= 100) { + status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] + if (!deletableTxStates.find((s) => s === status)) nonDeletableCount++ + transactions.push({status}) +} + +while (transactions40.length < 40) { + status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] + transactions40.push({status}) +} + +while (transactions20.length < 20) { + status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] + transactions20.push({status}) +} + + + +storage.data.TransactionController.transactions = transactions + +describe('storage is migrated successfully and the proper transactions are remove from state', () => { + it('should remove transactions that are unneeded', (done) => { + migration23.migrate(storage) + .then((migratedData) => { + let leftoverNonDeletableTxCount = 0 + const migratedTransactions = migratedData.data.TransactionController.transactions + migratedTransactions.forEach((tx) => { + if (!deletableTxStates.find((s) => s === tx.status)) { + leftoverNonDeletableTxCount++ + } + }) + assert.equal(leftoverNonDeletableTxCount, nonDeletableCount, 'migration shouldnt delete transactions we want to keep') + assert((migratedTransactions.length >= 40), `should be equal or greater to 40 if they are non deletable states got ${migratedTransactions.length} transactions`) + done() + }).catch(done) + }) + + it('should not remove any transactions because 40 is the expectable limit', (done) => { + storage.meta.version = 22 + storage.data.TransactionController.transactions = transactions40 + migration23.migrate(storage) + .then((migratedData) => { + const migratedTransactions = migratedData.data.TransactionController.transactions + + assert.equal(migratedTransactions.length, 40, 'migration shouldnt delete when at limit') + done() + }).catch(done) + }) + + it('should not remove any transactions because 20 txs is under the expectable limit', (done) => { + storage.meta.version = 22 + storage.data.TransactionController.transactions = transactions20 + migration23.migrate(storage) + .then((migratedData) => { + const migratedTransactions = migratedData.data.TransactionController.transactions + assert.equal(migratedTransactions.length, 20, 'migration shouldnt delete when under limit') + done() + }).catch(done) + }) + +}) diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 8970cf84d..5a27882ef 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -33,6 +33,42 @@ describe('Nonce Tracker', function () { }) }) + describe('sentry issue 476304902', function () { + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ status: 'submitted' }, { + fromNonce: 3, + count: 29, + }) + nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x3') + }) + + it('should return 9', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '32', `nonce should be 32 got ${nonceLock.nextNonce}`) + await nonceLock.releaseLock() + }) + }) + + describe('issue 3670', function () { + beforeEach(function () { + const txGen = new MockTxGen() + pendingTxs = txGen.generate({ status: 'submitted' }, { + fromNonce: 6, + count: 3, + }) + nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x6') + }) + + it('should return 9', async function () { + this.timeout(15000) + const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926') + assert.equal(nonceLock.nextNonce, '9', `nonce should be 9 got ${nonceLock.nextNonce}`) + await nonceLock.releaseLock() + }) + }) + describe('with no previous txs', function () { beforeEach(function () { nonceTracker = generateNonceTrackerWith([], []) diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js index 220bf501f..a5ac13664 100644 --- a/test/unit/tx-state-manager-test.js +++ b/test/unit/tx-state-manager-test.js @@ -240,12 +240,12 @@ describe('TransactionStateManager', function () { }) describe('#wipeTransactions', function () { - + const specificAddress = '0xaa' const otherAddress = '0xbb' it('should remove only the transactions from a specific address', function () { - + const txMetas = [ { id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId }, { id: 1, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId }, @@ -268,7 +268,7 @@ describe('TransactionStateManager', function () { { id: 1, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId }, { id: 2, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId }, ] - + txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop)) txStateManager.wipeTransactions(specificAddress) |