Merge branch 'develop' into detectTokenFeature

feature/default_network_editable
Esteban MIno 6 years ago
commit 0494b2fc8a
  1. 40
      .circleci/config.yml
  2. 16
      .eslintignore
  3. 5
      .eslintrc
  4. 2
      .nvmrc
  5. 3
      app/_locales/en/messages.json
  6. 4
      app/scripts/contentscript.js
  7. 6
      app/scripts/controllers/preferences.js
  8. 1
      app/scripts/metamask-controller.js
  9. 4
      app/scripts/migrations/023.js
  10. 2
      development/announcer.js
  11. 2
      development/backGroundConnectionModifiers.js
  12. 15
      development/beefy.js
  13. 4
      development/mock-dev.js
  14. 2
      development/mockExtension.js
  15. 2
      development/selector.js
  16. 15
      development/sourcemap-validator.js
  17. 3
      development/ui-dev.js
  18. 12
      development/verify-locale-strings.js
  19. 8
      development/version-bump.js
  20. 37
      gentests.js
  21. 53
      gulpfile.js
  22. 4
      mascara/example/server.js
  23. 2
      mascara/src/app/first-time/breadcrumbs.js
  24. 76
      mascara/src/app/first-time/buy-ether-screen.js
  25. 42
      mascara/src/app/shapeshift-form/index.js
  26. 2
      mascara/test/helpers.js
  27. 6
      mascara/test/index.js
  28. 2
      mascara/test/test-ui.js
  29. 16
      mascara/test/util/mascara-test-helper.js
  30. 11
      notices/notices.js
  31. 4
      old-ui/app/components/transaction-list-item.js
  32. 6
      old-ui/app/components/typed-message-renderer.js
  33. 21728
      package-lock.json
  34. 27
      package.json
  35. 66
      test/e2e/beta/contract-test/contract.js
  36. 8
      test/e2e/beta/contract-test/index.html
  37. 157
      test/e2e/beta/from-import-beta-ui.spec.js
  38. 14
      test/e2e/beta/helpers.js
  39. 444
      test/e2e/beta/metamask-beta-ui.spec.js
  40. 4
      test/e2e/beta/run-all.sh
  41. 11
      test/e2e/metamask.spec.js
  42. 2
      test/integration/lib/add-token.js
  43. 9
      test/integration/lib/confirm-sig-requests.js
  44. 2
      test/integration/lib/mascara-first-time.js
  45. 4
      test/integration/lib/send-new-ui.js
  46. 1
      test/integration/lib/tx-list-items.js
  47. 2
      test/lib/mock-encryptor.js
  48. 6
      test/lib/mock-tx-gen.js
  49. 100
      test/lib/react-trigger-change.js
  50. 2
      test/mascara.conf.js
  51. 10
      test/screens/new-ui.js
  52. 1
      test/unit/app/account-import-strategies.spec.js
  53. 2
      test/unit/app/controllers/address-book-controller.js
  54. 3
      test/unit/app/controllers/network-contoller-test.js
  55. 14
      test/unit/app/controllers/transactions/nonce-tracker-test.js
  56. 45
      test/unit/app/controllers/transactions/pending-tx-test.js
  57. 2
      test/unit/app/controllers/transactions/recipient-blacklist-checker-test.js
  58. 8
      test/unit/app/controllers/transactions/tx-controller-test.js
  59. 1
      test/unit/app/controllers/transactions/tx-gas-util-test.js
  60. 33
      test/unit/app/controllers/transactions/tx-state-history-helper-test.js
  61. 47
      test/unit/app/controllers/transactions/tx-state-manager-test.js
  62. 2
      test/unit/app/controllers/transactions/tx-utils-test.js
  63. 8
      test/unit/app/edge-encryptor-test.js
  64. 4
      test/unit/app/nodeify-test.js
  65. 7
      test/unit/app/pending-balance-test.js
  66. 77
      test/unit/app/seed-phrase-verifier-test.js
  67. 2
      test/unit/components/balance-component-test.js
  68. 4
      test/unit/components/bn-as-decimal-input-test.js
  69. 2
      test/unit/components/pending-tx-test.js
  70. 16
      test/unit/migrations/022-test.js
  71. 12
      test/unit/migrations/023-test.js
  72. 11
      test/unit/migrations/024-test.js
  73. 10
      test/unit/migrations/025-test.js
  74. 1
      test/unit/migrations/template-test.js
  75. 54
      test/unit/responsive/components/dropdown-test.js
  76. 21
      ui/app/actions.js
  77. 48
      ui/app/components/customize-gas-modal/index.js
  78. 49
      ui/app/components/ens-input.js
  79. 12
      ui/app/components/input-number.js
  80. 4
      ui/app/components/send/currency-display.js
  81. 4
      ui/app/components/send_/account-list-item/account-list-item.container.js
  82. 2
      ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
  83. 1
      ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
  84. 4
      ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
  85. 2
      ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
  86. 2
      ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js
  87. 4
      ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
  88. 2
      ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
  89. 6
      ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
  90. 6
      ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
  91. 12
      ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
  92. 6
      ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
  93. 2
      ui/app/components/send_/send.constants.js
  94. 5
      ui/app/components/send_/send.selectors.js
  95. 3
      ui/app/components/send_/send.utils.js
  96. 10
      ui/app/components/send_/tests/send-selectors.test.js
  97. 6
      ui/app/components/send_/tests/send-utils.test.js
  98. 2
      ui/app/components/shapeshift-form.js
  99. 3
      ui/app/components/signature-request.js
  100. 9
      ui/app/css/itcss/components/currency-display.scss
  101. Some files were not shown because too many files have changed in this diff Show More

@ -97,7 +97,7 @@ workflows:
jobs:
prep-deps-npm:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -116,7 +116,7 @@ jobs:
prep-deps-firefox:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- run:
@ -129,7 +129,7 @@ jobs:
prep-build:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -148,7 +148,7 @@ jobs:
prep-docs:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -163,7 +163,7 @@ jobs:
prep-scss:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -182,7 +182,7 @@ jobs:
test-lint:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -193,7 +193,7 @@ jobs:
test-deps:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -204,7 +204,7 @@ jobs:
test-e2e-chrome:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -220,7 +220,7 @@ jobs:
test-e2e-firefox:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -241,7 +241,7 @@ jobs:
test-e2e-beta-chrome:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -257,7 +257,7 @@ jobs:
test-e2e-beta-firefox:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -278,7 +278,7 @@ jobs:
job-screens:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -295,7 +295,7 @@ jobs:
job-publish-prerelease:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -322,7 +322,7 @@ jobs:
job-publish-release:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -345,7 +345,7 @@ jobs:
test-unit:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -358,7 +358,7 @@ jobs:
environment:
browsers: '["Firefox"]'
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -382,7 +382,7 @@ jobs:
environment:
browsers: '["Chrome"]'
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -401,7 +401,7 @@ jobs:
environment:
browsers: '["Firefox"]'
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -425,7 +425,7 @@ jobs:
environment:
browsers: '["Chrome"]'
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- restore_cache:
@ -442,7 +442,7 @@ jobs:
all-tests-pass:
docker:
- image: circleci/node:8-browsers
- image: circleci/node:8.11.3-browsers
steps:
- run:
name: All Tests Passed

@ -1,6 +1,20 @@
node_modules/**
dist/**
builds/**
docs/**
development/bundle.js
development/states.js
app/scripts/lib/extension-instance.js
app/scripts/chromereload.js
ui/lib/blockies.js
mascara/src/app/first-time/spinner.js
mascara/test/jquery-3.1.0.min.js
test/integration/bundle.js
test/integration/jquery-3.1.0.min.js
test/integration/helpers.js
test/integration/lib/first-time.js
ui/lib/blockies.js

@ -37,7 +37,9 @@
"document": false,
"navigator": false,
"web3": true,
"window": false
"window": false,
"$": false,
"QUnit": false
},
"rules": {
@ -159,5 +161,6 @@
"yield-star-spacing": [2, "both"],
"yoda": [2, "never"],
"prefer-const": 1,
"mocha/no-exclusive-tests": "error"
}
}

@ -1 +1 @@
v6.3.1
v8.11.3

@ -265,6 +265,9 @@
"encryptNewDen": {
"message": "Encrypt your new DEN"
},
"ensNameNotFound": {
"message": "ENS name not found"
},
"enterPassword": {
"message": "Enter password"
},

@ -115,8 +115,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
* @returns {boolean} {@code true} if Web3 should be injected
*/
function shouldInjectWeb3 () {
return doctypeCheck() && suffixCheck()
&& documentElementCheck() && !blacklistedDomainCheck()
return doctypeCheck() && suffixCheck() &&
documentElementCheck() && !blacklistedDomainCheck()
}
/**

@ -111,9 +111,9 @@ class PreferencesController {
* @returns {Promise<string>} selectedAddress the selected address.
*/
syncAddresses (addresses) {
let { identities, lostIdentities } = this.store.getState()
const { identities, lostIdentities } = this.store.getState()
let newlyLost = {}
const newlyLost = {}
Object.keys(identities).forEach((identity) => {
if (!addresses.includes(identity)) {
newlyLost[identity] = identities[identity]
@ -128,7 +128,7 @@ class PreferencesController {
if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
// store lost accounts
for (let key in newlyLost) {
for (const key in newlyLost) {
lostIdentities[key] = newlyLost[key]
}
}

@ -412,7 +412,6 @@ module.exports = class MetamaskController extends EventEmitter {
}
//=============================================================================
// VAULT / KEYRING RELATED METHODS
//=============================================================================

@ -35,10 +35,10 @@ function transformState (state) {
if (transactions.length <= 40) return newState
let reverseTxList = transactions.reverse()
const reverseTxList = transactions.reverse()
let stripping = true
while (reverseTxList.length > 40 && stripping) {
let txIndex = reverseTxList.findIndex((txMeta) => {
const txIndex = reverseTxList.findIndex((txMeta) => {
return (txMeta.status === 'failed' ||
txMeta.status === 'rejected' ||
txMeta.status === 'confirmed' ||

@ -7,6 +7,6 @@ var changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md')).toSt
var log = changelog.split(version)[1].split('##')[0].trim()
let msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`
const msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`
console.log(msg)

@ -1,5 +1,5 @@
module.exports = {
"confirm sig requests": {
'confirm sig requests': {
signMessage: (msgData, cb) => {
const stateUpdate = {
unapprovedMsgs: {},

@ -1,17 +1,14 @@
const beefy = require('beefy')
const http = require('http')
const fs = require('fs')
const path = require('path')
const port = 8124
const handler = beefy({
entries: {'mocker.js': 'bundle.js'}
, cwd: __dirname
, live: true
, open: true
, quiet: false
, bundlerFlags: ['-t', 'brfs']
entries: {'mocker.js': 'bundle.js'},
cwd: __dirname,
live: true,
open: true,
quiet: false,
bundlerFlags: ['-t', 'brfs'],
})

@ -12,7 +12,6 @@
* To use, run `npm run mock`.
*/
const extend = require('xtend')
const render = require('react-dom').render
const h = require('react-hyperscript')
const Root = require('../ui/app/root')
@ -24,7 +23,6 @@ const Selector = require('./selector')
const MetamaskController = require('../app/scripts/metamask-controller')
const firstTimeState = require('../app/scripts/first-time-state')
const ExtensionPlatform = require('../app/scripts/platforms/extension')
const extension = require('./mockExtension')
const noop = function () {}
const log = require('loglevel')
@ -81,7 +79,7 @@ const controller = new MetamaskController({
initState: firstTimeState,
})
global.metamaskController = controller
global.platform = new ExtensionPlatform
global.platform = new ExtensionPlatform()
//
// User Interface

@ -39,6 +39,6 @@ extension.runtime.reload = noop
extension.tabs.create = noop
extension.runtime.getManifest = function () {
return {
version: 'development'
version: 'development',
}
}

@ -11,7 +11,7 @@ function NewComponent () {
NewComponent.prototype.render = function () {
const props = this.props
let {
const {
states,
selectedKey,
actions,

@ -1,6 +1,6 @@
const fs = require('fs')
const { SourceMapConsumer } = require('source-map')
const path = require('path')
//
// Utility to help check if sourcemaps are working
//
@ -11,9 +11,11 @@ const { SourceMapConsumer } = require('source-map')
start()
async function start () {
const rawBuild = fs.readFileSync(__dirname + '/../dist/chrome/inpage.js', 'utf8')
const rawSourceMap = fs.readFileSync(__dirname + '/../dist/sourcemaps/inpage.js.map', 'utf8')
const rawBuild = fs.readFileSync(path.join(__dirname, '/../dist/chrome/', 'inpage.js')
, 'utf8')
const rawSourceMap = fs.readFileSync(path.join(__dirname, '/../dist/sourcemaps/', 'inpage.js.map'), 'utf8')
const consumer = await new SourceMapConsumer(rawSourceMap)
console.log('hasContentsOfAllSources:', consumer.hasContentsOfAllSources(), '\n')
@ -43,7 +45,8 @@ async function start() {
}
function indicesOf (substring, string) {
var a=[],i=-1;
while((i=string.indexOf(substring,i+1)) >= 0) a.push(i);
return a;
var a = []
var i = -1
while ((i = string.indexOf(substring, i + 1)) >= 0) a.push(i)
return a
}

@ -29,9 +29,8 @@ log.setDefaultLevel(1)
// Query String
const qs = require('qs')
let queryString = qs.parse(window.location.href.split('#')[1])
const queryString = qs.parse(window.location.href.split('#')[1])
let selectedView = queryString.view || 'first time'
const firstState = states[selectedView]
updateQueryParams(selectedView)
// CSS

@ -20,7 +20,7 @@ const specifiedLocale = process.argv[2]
if (specifiedLocale) {
console.log(`Verifying selected locale "${specifiedLocale}":\n\n`)
const locale = localeIndex.find(localeMeta => localeMeta.code === specifiedLocale)
verifyLocale({ localeMeta })
verifyLocale({ locale })
} else {
console.log('Verifying all locales:\n\n')
localeIndex.forEach(localeMeta => {
@ -30,16 +30,16 @@ if (specifiedLocale) {
}
function verifyLocale ({ localeMeta }) {
const localeCode = localeMeta.code
const localeName = localeMeta.name
let targetLocale, englishLocale
try {
const localeFilePath = path.join(process.cwd(), 'app', '_locales', localeCode, 'messages.json')
targetLocale = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'));
targetLocale = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'))
} catch (e) {
if (e.code == 'ENOENT') {
if (e.code === 'ENOENT') {
console.log('Locale file not found')
} else {
console.log(`Error opening your locale ("${localeCode}") file: `, e)
@ -49,9 +49,9 @@ function verifyLocale({ localeMeta }) {
try {
const englishFilePath = path.join(process.cwd(), 'app', '_locales', 'en', 'messages.json')
englishLocale = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'));
englishLocale = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'))
} catch (e) {
if(e.code == 'ENOENT') {
if (e.code === 'ENOENT') {
console.log('English File not found')
} else {
console.log('Error opening english locale file: ', e)

@ -19,13 +19,13 @@ async function versionBump(bumpType, changelog, oldManifest) {
return {
version: newVersion,
manifest: manifest,
changelog: logLines.join('\n')
changelog: logLines.join('\n'),
}
}
function newVersionFrom (manifest, bumpType) {
const string = manifest.version
let segments = string.split('.').map((str) => parseInt(str))
const segments = string.split('.').map((str) => parseInt(str))
switch (bumpType) {
case 'major':
@ -45,8 +45,4 @@ function newVersionFrom (manifest, bumpType) {
return segments.map(String).join('.')
}
function bumpManifest (manifest, bumpType) {
}
module.exports = versionBump

@ -1,6 +1,6 @@
const fs = require('fs')
const async = require('async')
const path = require('path')
const async = require('async')
const promisify = require('pify')
// start(/\.selectors.js/, generateSelectorTest).catch(console.error)
@ -8,7 +8,6 @@ const promisify = require('pify')
startContainer(/\.container.js/, generateContainerTest).catch(console.error)
async function getAllFileNames (dirName) {
const rootPath = path.join(__dirname, dirName)
const allNames = (await promisify(fs.readdir)(dirName))
const fileNames = allNames.filter(name => name.match(/^.+\./))
const dirNames = allNames.filter(name => name.match(/^[^.]+$/))
@ -16,7 +15,7 @@ async function getAllFileNames (dirName) {
const fullPathDirNames = dirNames.map(d => `${dirName}/${d}`)
const subNameArrays = await promisify(async.map)(fullPathDirNames, getAllFileNames)
let subNames = []
subNameArrays.forEach(subNameArray => subNames = [...subNames, ...subNameArray])
subNameArrays.forEach(subNameArray => { subNames = [...subNames, ...subNameArray] })
return [
...fileNames.map(name => dirName + '/' + name),
@ -24,6 +23,7 @@ async function getAllFileNames (dirName) {
]
}
/*
async function start (fileRegEx, testGenerator) {
const fileNames = await getAllFileNames('./ui/app')
const sFiles = fileNames.filter(name => name.match(fileRegEx))
@ -31,7 +31,7 @@ async function start (fileRegEx, testGenerator) {
let sFileMethodNames
let testFilePath
async.each(sFiles, async (sFile, cb) => {
let [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
const [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
sFileMethodNames = Object.keys(require(__dirname + '/' + sFile))
testFilePath = sPath.replace('.', '-').replace('.', '.test.')
@ -46,23 +46,26 @@ async function start (fileRegEx, testGenerator) {
})
}
*/
async function startContainer (fileRegEx, testGenerator) {
const fileNames = await getAllFileNames('./ui/app')
const sFiles = fileNames.filter(name => name.match(fileRegEx))
let sFileMethodNames
async.each(sFiles, async (sFile, cb) => {
console.log(`sFile`, sFile);
let [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
console.log(`sFile`, sFile)
const [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
let testFilePath = sPath.replace('.', '-').replace('.', '.test.')
const testFilePath = sPath.replace('.', '-').replace('.', '.test.')
await promisify(fs.readFile)(
__dirname + '/' + sFile,
path.join(__dirname, sFile),
'utf8',
async (err, result) => {
console.log(`result`, result.length);
if (err) {
console.log('Error: ', err)
} else {
console.log(`result`, result.length)
const returnObjectStrings = result
.match(/return\s(\{[\s\S]+?})\n}/g)
.map(str => {
@ -113,21 +116,22 @@ async function startContainer (fileRegEx, testGenerator) {
})
// console.log(`containerTest`, `${__dirname}/${sRootPath}tests/${testFilePath}`, containerTest);
console.log('----')
console.log(`sRootPath`, sRootPath);
console.log(`testFilePath`, testFilePath);
console.log(`sRootPath`, sRootPath)
console.log(`testFilePath`, testFilePath)
await promisify(fs.writeFile)(
`${__dirname}/${sRootPath}tests/${testFilePath}`,
containerTest,
'utf8'
)
}
}
)
}, (err) => {
console.log('123', err)
})
}
/*
function generateMethodList (methodArray) {
return methodArray.map(n => ' ' + n).join(',\n') + ','
}
@ -143,7 +147,7 @@ function generateMethodDescribeBlock (methodName, index) {
})`
return describeBlock
}
*/
function generateDispatchMethodDescribeBlock (methodName, index) {
const describeBlock =
`${index ? ' ' : ''}describe('${methodName}()', () => {
@ -154,12 +158,13 @@ function generateDispatchMethodDescribeBlock (methodName, index) {
})`
return describeBlock
}
/*
function generateMethodDescribeBlocks (methodArray) {
return methodArray
.map((methodName, index) => generateMethodDescribeBlock(methodName, index))
.join('\n\n')
}
*/
function generateDispatchMethodDescribeBlocks (methodArray) {
return methodArray
@ -167,6 +172,7 @@ function generateDispatchMethodDescribeBlocks (methodArray) {
.join('\n\n')
}
/*
function generateSelectorTest (name, methodArray) {
return `import assert from 'assert'
import {
@ -192,6 +198,7 @@ describe('${name.match(/^[^.]+/)} utils', () => {
})`
}
*/
function generateContainerTest (sPath, {
mapStateToPropsAssertionObject,

@ -13,21 +13,15 @@ const zip = require('gulp-zip')
const assign = require('lodash.assign')
const livereload = require('gulp-livereload')
const del = require('del')
const eslint = require('gulp-eslint')
const fs = require('fs')
const path = require('path')
const manifest = require('./app/manifest.json')
const replace = require('gulp-replace')
const mkdirp = require('mkdirp')
const asyncEach = require('async/each')
const exec = require('child_process').exec
const sass = require('gulp-sass')
const autoprefixer = require('gulp-autoprefixer')
const gulpStylelint = require('gulp-stylelint')
const stylefmt = require('gulp-stylefmt')
const uglify = require('gulp-uglify-es').default
const babel = require('gulp-babel')
const debug = require('gulp-debug')
const pify = require('pify')
const gulpMultiProcess = require('gulp-multi-process')
const endOfStream = pify(require('end-of-stream'))
@ -48,7 +42,7 @@ const commonPlatforms = [
// browser webapp
'mascara',
// browser extensions
...browserPlatforms
...browserPlatforms,
]
// browser reload
@ -165,11 +159,11 @@ gulp.task('manifest:opera', function() {
return gulp.src('./dist/opera/manifest.json')
.pipe(jsoneditor(function (json) {
json.permissions = [
"storage",
"tabs",
"clipboardWrite",
"clipboardRead",
"http://localhost:8545/"
'storage',
'tabs',
'clipboardWrite',
'clipboardRead',
'http://localhost:8545/',
]
return json
}))
@ -212,29 +206,6 @@ gulp.task('dev:copy',
)
)
// lint js
const lintTargets = ['app/**/*.json', 'app/**/*.js', '!app/scripts/vendor/**/*.js', 'ui/**/*.js', 'old-ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js']
gulp.task('lint', function () {
// Ignoring node_modules, dist/firefox, and docs folders:
return gulp.src(lintTargets)
.pipe(eslint(fs.readFileSync(path.join(__dirname, '.eslintrc'))))
// eslint.format() outputs the lint results to the console.
// Alternatively use eslint.formatEach() (see Docs).
.pipe(eslint.format())
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failAfterError last.
.pipe(eslint.failAfterError())
});
gulp.task('lint:fix', function () {
return gulp.src(lintTargets)
.pipe(eslint(Object.assign(fs.readFileSync(path.join(__dirname, '.eslintrc')), {fix: true})))
.pipe(eslint.format())
.pipe(eslint.failAfterError())
});
// scss compilation and autoprefixing tasks
gulp.task('build:scss', createScssBuildTask({
@ -277,17 +248,17 @@ gulp.task('lint-scss', function() {
.src('ui/app/css/itcss/**/*.scss')
.pipe(gulpStylelint({
reporters: [
{ formatter: 'string', console: true }
{ formatter: 'string', console: true },
],
fix: true,
}));
});
}))
})
gulp.task('fmt-scss', function () {
return gulp.src('ui/app/css/itcss/**/*.scss')
.pipe(stylefmt())
.pipe(gulp.dest('ui/app/css/itcss'));
});
.pipe(gulp.dest('ui/app/css/itcss'))
})
// build js
@ -562,7 +533,7 @@ function bundleTask(opts) {
buildStream = buildStream
.pipe(uglify({
mangle: {
reserved: [ 'MetamaskInpageProvider' ]
reserved: [ 'MetamaskInpageProvider' ],
},
}))
}

@ -1,8 +1,8 @@
const express = require('express')
const path = require('path')
const createMetamascaraServer = require('../server/')
const createBundle = require('../server/util').createBundle
const serveBundle = require('../server/util').serveBundle
//
// Iframe Server
//
@ -23,7 +23,7 @@ const dappServer = express()
// serve dapp bundle
serveBundle(dappServer, '/app.js', createBundle(require.resolve('./app.js')))
dappServer.use(express.static(__dirname + '/app/'))
dappServer.use(express.static(path.join(__dirname, '/app/')))
// start the server
const dappPort = '9002'

@ -20,7 +20,7 @@ export default class Breadcrumbs extends Component {
/>
))}
</div>
);
)
}
}

@ -54,7 +54,7 @@ class BuyEtherScreen extends Component {
return (
<div
className='buy-ether__do-it-later'
className="buy-ether__do-it-later"
onClick={() => showAccountDetail(address)}
>
Do it later
@ -64,17 +64,17 @@ class BuyEtherScreen extends Component {
renderCoinbaseLogo () {
return (
<svg width='140px' height='49px' viewBox='0 0 579 126' version='1.1'>
<g id='Page-1' stroke='none' strokeWidth={1} fill='none' fillRule='evenodd'>
<g id='Imported-Layers' fill='#0081C9'>
<path d='M37.752,125.873 C18.824,125.873 0.369,112.307 0.369,81.549 C0.369,50.79 18.824,37.382 37.752,37.382 C47.059,37.382 54.315,39.749 59.52,43.219 L53.841,55.68 C50.371,53.156 45.166,51.579 39.961,51.579 C28.604,51.579 18.193,60.57 18.193,81.391 C18.193,102.212 28.919,111.361 39.961,111.361 C45.166,111.361 50.371,109.783 53.841,107.26 L59.52,120.036 C54.157,123.664 47.059,125.873 37.752,125.873' id='Fill-1' />
<path d='M102.898,125.873 C78.765,125.873 65.515,106.786 65.515,81.549 C65.515,56.311 78.765,37.382 102.898,37.382 C127.032,37.382 140.282,56.311 140.282,81.549 C140.282,106.786 127.032,125.873 102.898,125.873 L102.898,125.873 Z M102.898,51.105 C89.491,51.105 82.866,63.093 82.866,81.391 C82.866,99.688 89.491,111.834 102.898,111.834 C116.306,111.834 122.931,99.688 122.931,81.391 C122.931,63.093 116.306,51.105 102.898,51.105 L102.898,51.105 Z' id='Fill-2' />
<path d='M163.468,23.659 C157.79,23.659 153.215,19.243 153.215,13.88 C153.215,8.517 157.79,4.1 163.468,4.1 C169.146,4.1 173.721,8.517 173.721,13.88 C173.721,19.243 169.146,23.659 163.468,23.659 L163.468,23.659 Z M154.793,39.118 L172.144,39.118 L172.144,124.138 L154.793,124.138 L154.793,39.118 Z' id='Fill-3' />
<path d='M240.443,124.137 L240.443,67.352 C240.443,57.415 234.449,51.263 222.619,51.263 C216.31,51.263 210.473,52.367 207.003,53.787 L207.003,124.137 L189.81,124.137 L189.81,43.376 C198.328,39.906 209.212,37.382 222.461,37.382 C246.28,37.382 257.794,47.793 257.794,65.775 L257.794,124.137 L240.443,124.137' id='Fill-4' />
<path d='M303.536,125.873 C292.494,125.873 281.611,123.191 274.986,119.879 L274.986,0.314 L292.179,0.314 L292.179,41.326 C296.28,39.433 302.905,37.856 308.741,37.856 C330.667,37.856 345.494,53.629 345.494,79.656 C345.494,111.676 328.931,125.873 303.536,125.873 L303.536,125.873 Z M305.744,51.263 C301.012,51.263 295.491,52.367 292.179,54.103 L292.179,109.941 C294.703,111.045 299.593,112.149 304.482,112.149 C318.205,112.149 328.301,102.685 328.301,80.918 C328.301,62.305 319.467,51.263 305.744,51.263 L305.744,51.263 Z' id='Fill-5' />
<path d='M392.341,125.873 C367.892,125.873 355.589,115.935 355.589,99.215 C355.589,75.555 380.826,71.296 406.537,69.876 L406.537,64.513 C406.537,53.787 399.439,50.001 388.555,50.001 C380.511,50.001 370.731,52.525 365.053,55.207 L360.636,43.376 C367.419,40.379 378.933,37.382 390.29,37.382 C410.638,37.382 422.942,45.269 422.942,66.248 L422.942,119.879 C416.79,123.191 404.329,125.873 392.341,125.873 L392.341,125.873 Z M406.537,81.391 C389.186,82.337 371.835,83.757 371.835,98.9 C371.835,107.89 378.776,113.411 391.868,113.411 C397.389,113.411 403.856,112.465 406.537,111.203 L406.537,81.391 L406.537,81.391 Z' id='Fill-6' />
<path d='M461.743,125.873 C451.806,125.873 441.395,123.191 435.244,119.879 L441.08,106.629 C445.496,109.31 454.803,112.149 461.27,112.149 C470.576,112.149 476.728,107.575 476.728,100.477 C476.728,92.748 470.261,89.751 461.586,86.596 C450.228,82.337 437.452,77.132 437.452,61.201 C437.452,47.162 448.336,37.382 467.264,37.382 C477.517,37.382 486.035,39.906 492.029,43.376 L486.665,55.364 C482.88,52.998 475.309,50.317 469.157,50.317 C460.166,50.317 455.118,55.049 455.118,61.201 C455.118,68.93 461.428,71.611 469.788,74.766 C481.618,79.183 494.71,84.072 494.71,100.635 C494.71,115.935 483.038,125.873 461.743,125.873' id='Fill-7' />
<path d='M578.625,81.233 L522.155,89.12 C523.89,104.42 533.828,112.149 548.182,112.149 C556.699,112.149 565.848,110.099 571.684,106.944 L576.732,119.879 C570.107,123.349 558.75,125.873 547.078,125.873 C520.262,125.873 505.277,108.679 505.277,81.549 C505.277,55.522 519.789,37.382 543.607,37.382 C565.69,37.382 578.782,51.894 578.782,74.766 C578.782,76.816 578.782,79.025 578.625,81.233 L578.625,81.233 Z M543.292,50.001 C530.042,50.001 521.367,60.097 521.051,77.763 L562.22,72.084 C562.062,57.257 554.649,50.001 543.292,50.001 L543.292,50.001 Z' id='Fill-8' />
<svg width="140px" height="49px" viewBox="0 0 579 126" version="1.1">
<g id="Page-1" stroke="none" strokeWidth={1} fill="none" fillRule="evenodd">
<g id="Imported-Layers" fill="#0081C9">
<path d="M37.752,125.873 C18.824,125.873 0.369,112.307 0.369,81.549 C0.369,50.79 18.824,37.382 37.752,37.382 C47.059,37.382 54.315,39.749 59.52,43.219 L53.841,55.68 C50.371,53.156 45.166,51.579 39.961,51.579 C28.604,51.579 18.193,60.57 18.193,81.391 C18.193,102.212 28.919,111.361 39.961,111.361 C45.166,111.361 50.371,109.783 53.841,107.26 L59.52,120.036 C54.157,123.664 47.059,125.873 37.752,125.873" id="Fill-1" />
<path d="M102.898,125.873 C78.765,125.873 65.515,106.786 65.515,81.549 C65.515,56.311 78.765,37.382 102.898,37.382 C127.032,37.382 140.282,56.311 140.282,81.549 C140.282,106.786 127.032,125.873 102.898,125.873 L102.898,125.873 Z M102.898,51.105 C89.491,51.105 82.866,63.093 82.866,81.391 C82.866,99.688 89.491,111.834 102.898,111.834 C116.306,111.834 122.931,99.688 122.931,81.391 C122.931,63.093 116.306,51.105 102.898,51.105 L102.898,51.105 Z" id="Fill-2" />
<path d="M163.468,23.659 C157.79,23.659 153.215,19.243 153.215,13.88 C153.215,8.517 157.79,4.1 163.468,4.1 C169.146,4.1 173.721,8.517 173.721,13.88 C173.721,19.243 169.146,23.659 163.468,23.659 L163.468,23.659 Z M154.793,39.118 L172.144,39.118 L172.144,124.138 L154.793,124.138 L154.793,39.118 Z" id="Fill-3" />
<path d="M240.443,124.137 L240.443,67.352 C240.443,57.415 234.449,51.263 222.619,51.263 C216.31,51.263 210.473,52.367 207.003,53.787 L207.003,124.137 L189.81,124.137 L189.81,43.376 C198.328,39.906 209.212,37.382 222.461,37.382 C246.28,37.382 257.794,47.793 257.794,65.775 L257.794,124.137 L240.443,124.137" id="Fill-4" />
<path d="M303.536,125.873 C292.494,125.873 281.611,123.191 274.986,119.879 L274.986,0.314 L292.179,0.314 L292.179,41.326 C296.28,39.433 302.905,37.856 308.741,37.856 C330.667,37.856 345.494,53.629 345.494,79.656 C345.494,111.676 328.931,125.873 303.536,125.873 L303.536,125.873 Z M305.744,51.263 C301.012,51.263 295.491,52.367 292.179,54.103 L292.179,109.941 C294.703,111.045 299.593,112.149 304.482,112.149 C318.205,112.149 328.301,102.685 328.301,80.918 C328.301,62.305 319.467,51.263 305.744,51.263 L305.744,51.263 Z" id="Fill-5" />
<path d="M392.341,125.873 C367.892,125.873 355.589,115.935 355.589,99.215 C355.589,75.555 380.826,71.296 406.537,69.876 L406.537,64.513 C406.537,53.787 399.439,50.001 388.555,50.001 C380.511,50.001 370.731,52.525 365.053,55.207 L360.636,43.376 C367.419,40.379 378.933,37.382 390.29,37.382 C410.638,37.382 422.942,45.269 422.942,66.248 L422.942,119.879 C416.79,123.191 404.329,125.873 392.341,125.873 L392.341,125.873 Z M406.537,81.391 C389.186,82.337 371.835,83.757 371.835,98.9 C371.835,107.89 378.776,113.411 391.868,113.411 C397.389,113.411 403.856,112.465 406.537,111.203 L406.537,81.391 L406.537,81.391 Z" id="Fill-6" />
<path d="M461.743,125.873 C451.806,125.873 441.395,123.191 435.244,119.879 L441.08,106.629 C445.496,109.31 454.803,112.149 461.27,112.149 C470.576,112.149 476.728,107.575 476.728,100.477 C476.728,92.748 470.261,89.751 461.586,86.596 C450.228,82.337 437.452,77.132 437.452,61.201 C437.452,47.162 448.336,37.382 467.264,37.382 C477.517,37.382 486.035,39.906 492.029,43.376 L486.665,55.364 C482.88,52.998 475.309,50.317 469.157,50.317 C460.166,50.317 455.118,55.049 455.118,61.201 C455.118,68.93 461.428,71.611 469.788,74.766 C481.618,79.183 494.71,84.072 494.71,100.635 C494.71,115.935 483.038,125.873 461.743,125.873" id="Fill-7" />
<path d="M578.625,81.233 L522.155,89.12 C523.89,104.42 533.828,112.149 548.182,112.149 C556.699,112.149 565.848,110.099 571.684,106.944 L576.732,119.879 C570.107,123.349 558.75,125.873 547.078,125.873 C520.262,125.873 505.277,108.679 505.277,81.549 C505.277,55.522 519.789,37.382 543.607,37.382 C565.69,37.382 578.782,51.894 578.782,74.766 C578.782,76.816 578.782,79.025 578.625,81.233 L578.625,81.233 Z M543.292,50.001 C530.042,50.001 521.367,60.097 521.051,77.763 L562.22,72.084 C562.062,57.257 554.649,50.001 543.292,50.001 L543.292,50.001 Z" id="Fill-8" />
</g>
</g>
</svg>
@ -85,13 +85,13 @@ class BuyEtherScreen extends Component {
const {goToCoinbase, address} = this.props
return (
<div className='buy-ether__action-content-wrapper'>
<div className="buy-ether__action-content-wrapper">
<div>{this.renderCoinbaseLogo()}</div>
<div className='buy-ether__body-text'>Coinbase is the worlds most popular way to buy and sell bitcoin, ethereum, and litecoin.</div>
<a className='first-time-flow__link buy-ether__faq-link'>What is Ethereum?</a>
<div className='buy-ether__buttons'>
<div className="buy-ether__body-text">Coinbase is the worlds most popular way to buy and sell bitcoin, ethereum, and litecoin.</div>
<a className="first-time-flow__link buy-ether__faq-link">What is Ethereum?</a>
<div className="buy-ether__buttons">
<button
className='first-time-flow__button'
className="first-time-flow__button"
onClick={() => goToCoinbase(address)}
>
Buy
@ -114,23 +114,23 @@ class BuyEtherScreen extends Component {
return this.renderCoinbaseForm()
case OPTION_VALUES.SHAPESHIFT:
return (
<div className='buy-ether__action-content-wrapper'>
<div className='shapeshift-logo' />
<div className='buy-ether__body-text'>
<div className="buy-ether__action-content-wrapper">
<div className="shapeshift-logo" />
<div className="buy-ether__body-text">
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
</div>
<ShapeShiftForm btnClass='first-time-flow__button' />
<ShapeShiftForm btnClass="first-time-flow__button" />
</div>
)
case OPTION_VALUES.QR_CODE:
return (
<div className='buy-ether__action-content-wrapper'>
<div className="buy-ether__action-content-wrapper">
<div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
<div className='buy-ether__body-text'>Deposit Ether directly into your account.</div>
<div className='buy-ether__small-body-text'>(This is the account address that MetaMask created for you to recieve funds.)</div>
<div className='buy-ether__buttons'>
<div className="buy-ether__body-text">Deposit Ether directly into your account.</div>
<div className="buy-ether__small-body-text">(This is the account address that MetaMask created for you to recieve funds.)</div>
<div className="buy-ether__buttons">
<button
className='first-time-flow__button'
className="first-time-flow__button"
onClick={this.copyToClipboard}
disabled={justCopied}
>
@ -149,19 +149,19 @@ class BuyEtherScreen extends Component {
const { selectedOption } = this.state
return (
<div className='buy-ether'>
<div className="buy-ether">
<Identicon address={this.props.address} diameter={70} />
<div className='buy-ether__title'>Deposit Ether</div>
<div className='buy-ether__body-text'>
<div className="buy-ether__title">Deposit Ether</div>
<div className="buy-ether__body-text">
MetaMask works best if you have Ether in your account to pay for transaction gas fees and more. To get Ether, choose from one of these methods.
</div>
<div className='buy-ether__content-wrapper'>
<div className='buy-ether__content-headline-wrapper'>
<div className='buy-ether__content-headline'>Deposit Options</div>
<div className="buy-ether__content-wrapper">
<div className="buy-ether__content-headline-wrapper">
<div className="buy-ether__content-headline">Deposit Options</div>
{this.renderSkip()}
</div>
<div className='buy-ether__content'>
<div className='buy-ether__side-panel'>
<div className="buy-ether__content">
<div className="buy-ether__side-panel">
{OPTIONS.map(({ name, value }) => (
<div
key={value}
@ -170,16 +170,16 @@ class BuyEtherScreen extends Component {
})}
onClick={() => this.setState({ selectedOption: value })}
>
<div className='buy-ether__side-panel-item-name'>{name}</div>
<div className="buy-ether__side-panel-item-name">{name}</div>
{value === selectedOption && (
<svg viewBox='0 0 574 1024' id='si-ant-right' width='15px' height='15px'>
<path d='M10 9Q0 19 0 32t10 23l482 457L10 969Q0 979 0 992t10 23q10 9 24 9t24-9l506-480q10-10 10-23t-10-23L58 9Q48 0 34 0T10 9z' />
<svg viewBox="0 0 574 1024" id="si-ant-right" width="15px" height="15px">
<path d="M10 9Q0 19 0 32t10 23l482 457L10 969Q0 979 0 992t10 23q10 9 24 9t24-9l506-480q10-10 10-23t-10-23L58 9Q48 0 34 0T10 9z" />
</svg>
)}
</div>
))}
</div>
<div className='buy-ether__action-content'>
<div className="buy-ether__action-content">
{this.renderContent()}
</div>
</div>

@ -79,11 +79,11 @@ export class ShapeShiftForm extends Component {
renderMetadata (label, value) {
return (
<div className='shapeshift-form__metadata-wrapper'>
<div className='shapeshift-form__metadata-label'>
<div className="shapeshift-form__metadata-wrapper">
<div className="shapeshift-form__metadata-label">
{label}:
</div>
<div className='shapeshift-form__metadata-value'>
<div className="shapeshift-form__metadata-value">
{value}
</div>
</div>
@ -101,7 +101,7 @@ export class ShapeShiftForm extends Component {
} = tokenExchangeRates[coinPair] || {}
return (
<div className='shapeshift-form__metadata'>
<div className="shapeshift-form__metadata">
{this.renderMetadata('Status', limit ? 'Available' : 'Unavailable')}
{this.renderMetadata('Limit', limit)}
{this.renderMetadata('Exchange Rate', rate)}
@ -117,13 +117,13 @@ export class ShapeShiftForm extends Component {
qrImage.make()
return (
<div className='shapeshift-form'>
<div className='shapeshift-form__deposit-instruction'>
<div className="shapeshift-form">
<div className="shapeshift-form__deposit-instruction">
Deposit your BTC to the address bellow:
</div>
<div className='shapeshift-form__qr-code'>
<div className="shapeshift-form__qr-code">
{isLoading
? <img src='images/loading.svg' style={{ width: '60px' }} />
? <img src="images/loading.svg" style={{ width: '60px' }} />
: <div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
}
</div>
@ -141,14 +141,14 @@ export class ShapeShiftForm extends Component {
return showQrCode ? this.renderQrCode() : (
<div>
<div className='shapeshift-form'>
<div className='shapeshift-form__selectors'>
<div className='shapeshift-form__selector'>
<div className='shapeshift-form__selector-label'>
<div className="shapeshift-form">
<div className="shapeshift-form__selectors">
<div className="shapeshift-form__selector">
<div className="shapeshift-form__selector-label">
Deposit
</div>
<select
className='shapeshift-form__selector-input'
className="shapeshift-form__selector-input"
value={this.state.depositCoin}
onChange={this.onCoinChange}
>
@ -160,14 +160,14 @@ export class ShapeShiftForm extends Component {
</select>
</div>
<div
className='icon shapeshift-form__caret'
className="icon shapeshift-form__caret"
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
/>
<div className='shapeshift-form__selector'>
<div className='shapeshift-form__selector-label'>
<div className="shapeshift-form__selector">
<div className="shapeshift-form__selector-label">
Receive
</div>
<div className='shapeshift-form__selector-input'>
<div className="shapeshift-form__selector-input">
ETH
</div>
</div>
@ -177,18 +177,18 @@ export class ShapeShiftForm extends Component {
'shapeshift-form__address-input-wrapper--error': errorMessage,
})}
>
<div className='shapeshift-form__address-input-label'>
<div className="shapeshift-form__address-input-label">
Your Refund Address
</div>
<input
type='text'
className='shapeshift-form__address-input'
type="text"
className="shapeshift-form__address-input"
onChange={e => this.setState({
refundAddress: e.target.value,
errorMessage: '',
})}
/>
<div className='shapeshift-form__address-input-error-message'>
<div className="shapeshift-form__address-input-error-message">
{errorMessage}
</div>
</div>

@ -1,4 +1,4 @@
function wait(time) {
export default function wait (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve()

@ -1,9 +1,9 @@
var fs = require('fs')
var path = require('path')
var browserify = require('browserify');
var browserify = require('browserify')
var tests = fs.readdirSync(path.join(__dirname, 'lib'))
var bundlePath = path.join(__dirname, 'test-bundle.js')
var b = browserify();
var b = browserify()
// Remove old bundle
try {
@ -18,5 +18,5 @@ tests.forEach(function(fileName) {
b.add(path.join(__dirname, 'lib', fileName))
})
b.bundle().pipe(writeStream);
b.bundle().pipe(writeStream)

@ -1,5 +1,3 @@
const Helper = require('./util/mascara-test-helper.js')
window.addEventListener('load', () => {
window.METAMASK_SKIP_RELOAD = true
// inject app container

@ -2,23 +2,25 @@ const EventEmitter = require('events')
const IDB = require('idb-global')
const KEY = 'metamask-test-config'
module.exports = class Helper extends EventEmitter {
constructor () {
super()
}
tryToCleanContext () {
this.unregister()
.then(() => this.clearDb())
.then(() => super.emit('complete'))
.catch((err) => super.emit('complete'))
.catch((err) => {
if (err) {
super.emit('complete')
}
})
}
unregister () {
return global.navigator.serviceWorker.getRegistration()
.then((registration) => {
if (registration) return registration.unregister()
if (registration) {
return registration.unregister()
.then((b) => b ? Promise.resolve() : Promise.reject())
else return Promise.resolve()
} else return Promise.resolve()
})
}
clearDb () {
@ -33,7 +35,7 @@ module.exports = class Helper extends EventEmitter {
const db = new IDB({
version: 2,
key: KEY,
initialState: state
initialState: state,
})
return db.open()
}

@ -1,5 +1,6 @@
// fs.readFileSync is inlined by browserify transform "brfs"
const fs = require('fs')
const path = require('path')
module.exports = [
{
@ -7,14 +8,14 @@ module.exports = [
read: false,
date: 'Thu Feb 09 2017',
title: 'Terms of Use',
body: fs.readFileSync(__dirname + '/archive/notice_0.md', 'utf8'),
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_0.md'), 'utf8'),
},
{
id: 2,
read: false,
date: 'Mon May 08 2017',
title: 'Privacy Notice',
body: fs.readFileSync(__dirname + '/archive/notice_2.md', 'utf8'),
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_2.md'), 'utf8'),
},
{
id: 3,
@ -22,13 +23,13 @@ module.exports = [
date: 'Tue Nov 28 2017',
title: 'Seed Phrase Alert',
firstVersion: '<=3.12.0',
body: fs.readFileSync(__dirname + '/archive/notice_3.md', 'utf8'),
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_3.md'), 'utf8'),
},
{
id: 4,
read: false,
date: 'Wed Jun 13 2018',
title: 'Phishing Warning',
body: fs.readFileSync(__dirname + '/archive/notice_4.md', 'utf8'),
}
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_4.md'), 'utf8'),
},
]

@ -40,8 +40,8 @@ TransactionListItem.prototype.showRetryButton = function () {
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
&& lastSubmittedTxWithCurrentNonce.id === transaction.id
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
lastSubmittedTxWithCurrentNonce.id === transaction.id
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
}

@ -34,9 +34,13 @@ TypedMessageRenderer.prototype.render = function () {
function renderTypedData (values) {
return values.map(function (value) {
let v = value.value
if (typeof v === 'boolean') {
v = v.toString()
}
return h('div', {}, [
h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
h('div', {}, value.value),
h('div', {}, v),
])
})
}

21728
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -38,8 +38,8 @@
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
"ganache:start": "ganache-cli -m 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'",
"sentry:publish": "node ./development/sentry-publish.js",
"lint": "gulp lint",
"lint:fix": "gulp lint:fix",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
@ -93,7 +93,7 @@
"ensnare": "^1.0.0",
"eslint-plugin-react": "^7.4.0",
"eth-bin-to-ops": "^1.0.1",
"eth-contract-metadata": "^1.1.5",
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#master",
"eth-hd-keyring": "^1.2.1",
"eth-json-rpc-filters": "^1.2.6",
"eth-json-rpc-infura": "^3.0.0",
@ -107,7 +107,7 @@
"ethereumjs-wallet": "^0.6.0",
"etherscan-link": "^1.0.2",
"ethjs": "^0.4.0",
"ethjs-contract": "^0.2.0",
"ethjs-contract": "^0.2.3",
"ethjs-ens": "^2.0.0",
"ethjs-query": "^0.3.4",
"express": "^4.15.5",
@ -183,6 +183,7 @@
"semaphore": "^1.0.5",
"semver": "^5.4.1",
"shallow-copy": "0.0.1",
"superstatic": "^5.0.2",
"sw-controller": "^1.0.3",
"sw-stream": "^2.0.2",
"textarea-caret": "^3.0.1",
@ -208,7 +209,7 @@
"babel-register": "^6.7.2",
"babelify": "^8.0.0",
"beefy": "^2.1.5",
"brfs": "^1.4.3",
"brfs": "^1.6.1",
"browserify": "^16.1.1",
"chai": "^4.1.0",
"chromedriver": "2.36.0",
@ -232,13 +233,12 @@
"file-loader": "^1.1.11",
"fs-promise": "^2.0.3",
"ganache-cli": "^6.1.0",
"ganache-core": "^2.1.0",
"ganache-core": "^2.1.3",
"geckodriver": "^1.11.0",
"gh-pages": "^1.1.0",
"gh-pages": "^1.2.0",
"gifencoder": "^1.1.0",
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
"gulp-babel": "^7.0.0",
"gulp-eslint": "^4.0.0",
"gulp-json-editor": "^2.2.1",
"gulp-livereload": "^3.8.1",
"gulp-multi-process": "^1.3.1",
@ -251,13 +251,14 @@
"gulp-util": "^3.0.7",
"gulp-watch": "^5.0.0",
"gulp-zip": "^4.0.0",
"http-server": "^0.11.1",
"image-size": "^0.6.2",
"isomorphic-fetch": "^2.2.1",
"jsdoc": "^3.5.5",
"jsdom": "^11.2.0",
"jsdom-global": "^3.0.2",
"jshint-stylish": "~2.2.1",
"karma": "^2.0.0",
"karma": "^2.0.4",
"karma-chrome-launcher": "^2.2.0",
"karma-cli": "^1.0.1",
"karma-firefox-launcher": "^1.0.1",
@ -268,9 +269,9 @@
"mocha-jsdom": "^1.1.0",
"mocha-sinon": "^2.0.0",
"nock": "^9.0.14",
"node-sass": "^4.7.2",
"node-sass": "^4.9.0",
"nsp": "^3.2.1",
"nyc": "^11.0.3",
"nyc": "^13.0.0",
"open": "0.0.5",
"path": "^0.12.7",
"png-file-stream": "^1.0.0",
@ -293,11 +294,11 @@
"style-loader": "^0.21.0",
"stylelint-config-standard": "^18.2.0",
"tape": "^4.5.1",
"testem": "^2.0.0",
"testem": "^2.8.0",
"through2": "^2.0.3",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.9.0"
"watchify": "^3.11.0"
},
"engines": {
"node": ">=8.0.0"

@ -0,0 +1,66 @@
/*
The `piggybankContract` is compiled from:
pragma solidity ^0.4.0;
contract PiggyBank {
uint private balance;
address public owner;
function PiggyBank() public {
owner = msg.sender;
balance = 0;
}
function deposit() public payable returns (uint) {
balance += msg.value;
return balance;
}
function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
require(msg.sender == owner);
balance -= withdrawAmount;
msg.sender.transfer(withdrawAmount);
return balance;
}
}
*/
var piggybankContract = web3.eth.contract([{'constant': false, 'inputs': [{'name': 'withdrawAmount', 'type': 'uint256'}], 'name': 'withdraw', 'outputs': [{'name': 'remainingBal', 'type': 'uint256'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [], 'name': 'deposit', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': true, 'stateMutability': 'payable', 'type': 'function'}, {'inputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}])
const deployButton = document.getElementById('deployButton')
const depositButton = document.getElementById('depositButton')
const withdrawButton = document.getElementById('withdrawButton')
deployButton.addEventListener('click', async function (event) {
var piggybank = await piggybankContract.new(
{
from: web3.eth.accounts[0],
data: '0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000808190555061023b806100686000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e1a7d4d1461005c5780638da5cb5b1461009d578063d0e30db0146100f4575b600080fd5b34801561006857600080fd5b5061008760048036038101908080359060200190929190505050610112565b6040518082815260200191505060405180910390f35b3480156100a957600080fd5b506100b26101d0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100fc6101f6565b6040518082815260200191505060405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561017057600080fd5b8160008082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f193505050501580156101c5573d6000803e3d6000fd5b506000549050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003460008082825401925050819055506000549050905600a165627a7a72305820f237db3ec816a52589d82512117bc85bc08d3537683ffeff9059108caf3e5d400029',
gas: '4700000',
}, function (e, contract) {
console.log(e, contract)
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash)
console.log(`contract`, contract)
depositButton.addEventListener('click', function (event) {
contract.deposit({ from: web3.eth.accounts[0], value: '0x29a2241af62c0000' }, function (result) {
console.log(result)
})
})
withdrawButton.addEventListener('click', function (event) {
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
console.log(result)
})
})
}
})
console.log(piggybank)
})

@ -0,0 +1,8 @@
<html>
<body>
<button id="deployButton">Deploy Contract</button>
<button id="depositButton">Deposit</button>
<button id="withdrawButton">Withdraw</button>
</body>
<script src="contract.js"></script>
</html>

@ -12,7 +12,6 @@ const {
} = require('../func')
const {
checkBrowserForConsoleErrors,
loadExtension,
verboseReportOnFailure,
findElement,
findElements,
@ -22,13 +21,12 @@ const {
describe('Using MetaMask with an existing account', function () {
let extensionId
let driver
let tokenAddress
const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'
const testAddress = '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC'
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
const waitingNewPageDelayMs = regularDelayMs * 10
this.timeout(0)
this.bail(true)
@ -97,15 +95,16 @@ describe('Using MetaMask with an existing account', function () {
await delay(regularDelayMs)
// Close all other tabs
let [oldUi, infoPage, newUi] = await driver.getAllWindowHandles()
newUi = newUi || infoPage
const [oldUi, infoPage, newUi] = await driver.getAllWindowHandles()
const newUiOrInfoPage = newUi || infoPage
await driver.switchTo().window(oldUi)
await driver.close()
if (infoPage !== newUi) {
if (infoPage !== newUiOrInfoPage) {
await driver.switchTo().window(infoPage)
await driver.close()
}
await driver.switchTo().window(newUi)
await driver.switchTo().window(newUiOrInfoPage)
await delay(regularDelayMs)
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
@ -166,8 +165,7 @@ describe('Using MetaMask with an existing account', function () {
describe('Show account information', () => {
it('shows the correct account address', async () => {
const detailsButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Details')]`))
detailsButton.click()
await driver.findElement(By.css('.wallet-view__details-button')).click()
await driver.findElement(By.css('.qr-wrapper')).isDisplayed()
await delay(regularDelayMs)
@ -263,8 +261,10 @@ describe('Using MetaMask with an existing account', function () {
await configureGas.click()
await delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
await delay(regularDelayMs)
// Continue to next screen
@ -289,143 +289,36 @@ describe('Using MetaMask with an existing account', function () {
})
})
describe('Send ETH from Faucet', () => {
it('starts a send transaction inside Faucet', async () => {
await driver.executeScript('window.open("https://faucet.metamask.io")')
await delay(waitingNewPageDelayMs)
const [extension, faucet] = await driver.getAllWindowHandles()
await driver.switchTo().window(faucet)
await delay(regularDelayMs)
const send1eth = await findElement(driver, By.xpath(`//button[contains(text(), '10 ether')]`), 14000)
await send1eth.click()
await delay(regularDelayMs)
await driver.switchTo().window(extension)
await loadExtension(driver, extensionId)
await delay(regularDelayMs)
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`), 14000)
await confirmButton.click()
await delay(regularDelayMs)
await driver.switchTo().window(faucet)
await delay(regularDelayMs)
await driver.close()
await delay(regularDelayMs)
await driver.switchTo().window(extension)
await delay(regularDelayMs)
await loadExtension(driver, extensionId)
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)
})
})
describe('Add existing token using search', () => {
it('clicks on the Add Token button', async () => {
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
await addToken.click()
const [importAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Import Account')]`))
await importAccount.click()
await delay(regularDelayMs)
})
it('picks an existing token', async () => {
const tokenSearch = await findElement(driver, By.css('#search-tokens'))
await tokenSearch.sendKeys('BAT')
it('enter private key', async () => {
const privateKeyInput = await findElement(driver, By.css('#private-key-box'))
await privateKeyInput.sendKeys(testPrivateKey2)
await delay(regularDelayMs)
const token = await findElement(driver, By.xpath("//span[contains(text(), 'BAT')]"))
await token.click()
const importButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
await importButtons[0].click()
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
const addTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Tokens')]`))
await addTokens.click()
await delay(largeDelayMs)
})
it('renders the balance for the new token', async () => {
const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /^0\s*BAT\s*$/), 10000)
const tokenAmount = await balance.getText()
assert.ok(/^0\s*BAT\s*$/.test(tokenAmount))
await delay(regularDelayMs)
})
})
describe('Add a custom token from TokenFactory', () => {
it('creates a new token', async () => {
await driver.executeScript('window.open("https://tokenfactory.surge.sh/#/factory")')
await delay(waitingNewPageDelayMs)
const [extension, tokenFactory] = await driver.getAllWindowHandles()
await driver.switchTo().window(tokenFactory)
const [
totalSupply,
tokenName,
tokenDecimal,
tokenSymbol,
] = await findElements(driver, By.css('.form-control'))
await totalSupply.sendKeys('100')
await tokenName.sendKeys('Test')
await tokenDecimal.sendKeys('0')
await tokenSymbol.sendKeys('TST')
const createToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Create Token')]`))
await createToken.click()
await delay(regularDelayMs)
await driver.switchTo().window(extension)
await loadExtension(driver, extensionId)
await delay(regularDelayMs)
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
await driver.switchTo().window(tokenFactory)
await delay(regularDelayMs)
const tokenContactAddress = await driver.findElement(By.css('div > div > div:nth-child(2) > span:nth-child(3)'))
tokenAddress = await tokenContactAddress.getText()
await driver.close()
await driver.switchTo().window(extension)
await loadExtension(driver, extensionId)
it('should show the correct account name', async () => {
const [accountName] = await findElements(driver, By.css('.account-name'))
assert.equal(await accountName.getText(), 'Account 3')
await delay(regularDelayMs)
})
it('clicks on the Add Token button', async () => {
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
await addToken.click()
it('should show the imported label', async () => {
const [importedLabel] = await findElements(driver, By.css('.wallet-view__keyring-label'))
assert.equal(await importedLabel.getText(), 'IMPORTED')
await delay(regularDelayMs)
})
it('picks the new Test token', async () => {
const addCustomToken = await findElement(driver, By.xpath("//div[contains(text(), 'Custom Token')]"))
await addCustomToken.click()
await delay(regularDelayMs)
const newTokenAddress = await findElement(driver, By.css('#custom-address'))
await newTokenAddress.sendKeys(tokenAddress)
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
const addTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Tokens')]`))
await addTokens.click()
await delay(regularDelayMs)
})
it('renders the balance for the new token', async () => {
const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /^100\s*TST\s*$/), 10000)
const tokenAmount = await balance.getText()
assert.ok(/^100\s*TST\s*$/.test(tokenAmount))
await delay(regularDelayMs)
})
})
})

@ -2,6 +2,7 @@ const fs = require('fs')
const mkdirp = require('mkdirp')
const pify = require('pify')
const {until} = require('selenium-webdriver')
const { delay } = require('../func')
module.exports = {
checkBrowserForConsoleErrors,
@ -9,6 +10,7 @@ module.exports = {
verboseReportOnFailure,
findElement,
findElements,
openNewPage,
}
async function loadExtension (driver, extensionId) {
@ -64,3 +66,15 @@ async function findElement (driver, by, timeout = 10000) {
async function findElements (driver, by, timeout = 10000) {
return driver.wait(until.elementsLocated(by), timeout)
}
async function openNewPage (driver, url) {
await driver.executeScript('window.open()')
await delay(1000)
const handles = await driver.getAllWindowHandles()
const secondHandle = handles[1]
await driver.switchTo().window(secondHandle)
await driver.get(url)
await delay(1000)
}

@ -16,6 +16,7 @@ const {
checkBrowserForConsoleErrors,
loadExtension,
verboseReportOnFailure,
openNewPage,
} = require('./helpers')
describe('MetaMask', function () {
@ -27,7 +28,6 @@ describe('MetaMask', function () {
const tinyDelayMs = 1000
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
const waitingNewPageDelayMs = regularDelayMs * 30
this.timeout(0)
this.bail(true)
@ -62,7 +62,7 @@ describe('MetaMask', function () {
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(this.currentTest)
await verboseReportOnFailure(driver, this.currentTest)
}
})
@ -71,14 +71,20 @@ describe('MetaMask', function () {
})
describe('New UI setup', async function () {
let networkSelector
it('switches to first tab', async function () {
const [firstTab] = await driver.getAllWindowHandles()
await driver.switchTo().window(firstTab)
await delay(regularDelayMs)
try {
networkSelector = await findElement(driver, By.css('#network_component'))
} catch (e) {
await loadExtension(driver, extensionId)
}
await delay(regularDelayMs)
})
it('use the local network', async function () {
const networkSelector = await findElement(driver, By.css('#network_component'))
await networkSelector.click()
await delay(regularDelayMs)
@ -93,15 +99,21 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
// Close all other tabs
let [oldUi, infoPage, newUi] = await driver.getAllWindowHandles()
newUi = newUi || infoPage
const [oldUi, tab1, tab2] = await driver.getAllWindowHandles()
await driver.switchTo().window(oldUi)
await driver.close()
if (infoPage !== newUi) {
await driver.switchTo().window(infoPage)
await driver.switchTo().window(tab1)
const tab1Url = await driver.getCurrentUrl()
if (tab1Url.match(/metamask.io/)) {
await driver.switchTo().window(tab1)
await driver.close()
await driver.switchTo().window(tab2)
} else if (tab2) {
await driver.switchTo().window(tab2)
await driver.close()
await driver.switchTo().window(tab1)
}
await driver.switchTo().window(newUi)
await delay(regularDelayMs)
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
@ -136,6 +148,7 @@ describe('MetaMask', function () {
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('.tou button'))
driver.wait(until.elementIsEnabled(acceptTos))
await acceptTos.click()
await delay(regularDelayMs)
})
@ -160,8 +173,10 @@ describe('MetaMask', function () {
let seedPhrase
it('reveals the seed phrase', async () => {
const revealSeedPhrase = await findElement(driver, By.css('.backup-phrase__secret-blocker'))
await revealSeedPhrase.click()
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText()
@ -173,56 +188,76 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})
it('can retype the seed phrase', async () => {
const words = seedPhrase.split(' ')
async function retypeSeedPhrase (words) {
try {
const word0 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[0]}')]`), 10000)
const word0 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[0]}')]`))
await word0.click()
await delay(tinyDelayMs)
const word1 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[1]}')]`))
const word1 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[1]}')]`), 10000)
await word1.click()
await delay(tinyDelayMs)
const word2 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[2]}')]`))
const word2 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[2]}')]`), 10000)
await word2.click()
await delay(tinyDelayMs)
const word3 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[3]}')]`))
const word3 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[3]}')]`), 10000)
await word3.click()
await delay(tinyDelayMs)
const word4 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[4]}')]`))
const word4 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[4]}')]`), 10000)
await word4.click()
await delay(tinyDelayMs)
const word5 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[5]}')]`))
const word5 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[5]}')]`), 10000)
await word5.click()
await delay(tinyDelayMs)
const word6 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[6]}')]`))
const word6 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[6]}')]`), 10000)
await word6.click()
await delay(tinyDelayMs)
const word7 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[7]}')]`))
const word7 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[7]}')]`), 10000)
await word7.click()
await delay(tinyDelayMs)
const word8 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[8]}')]`))
const word8 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[8]}')]`), 10000)
await word8.click()
await delay(tinyDelayMs)
const word9 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[9]}')]`))
const word9 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[9]}')]`), 10000)
await word9.click()
await delay(tinyDelayMs)
const word10 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[10]}')]`))
const word10 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[10]}')]`), 10000)
await word10.click()
await delay(tinyDelayMs)
const word11 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[11]}')]`))
const word11 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[11]}')]`), 10000)
await word11.click()
await delay(tinyDelayMs)
} catch (e) {
await loadExtension(driver, extensionId)
await retypeSeedPhrase(words)
}
}
it('can retype the seed phrase', async () => {
const words = seedPhrase.split(' ')
await retypeSeedPhrase(words)
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click()
@ -230,7 +265,8 @@ describe('MetaMask', function () {
})
it('clicks through the deposit modal', async () => {
const buyModal = await driver.findElement(By.css('span .modal'))
const byBuyModal = By.css('span .modal')
const buyModal = await driver.wait(until.elementLocated(byBuyModal))
const closeModal = await findElement(driver, By.css('.page-container__header-close'))
await closeModal.click()
await driver.wait(until.stalenessOf(buyModal))
@ -244,8 +280,12 @@ describe('MetaMask', function () {
await driver.findElement(By.css('.qr-wrapper')).isDisplayed()
await delay(regularDelayMs)
const accountModal = await driver.findElement(By.css('span .modal'))
await driver.executeScript("document.querySelector('.account-modal-close').click()")
await delay(regularDelayMs * 4)
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
})
})
@ -345,8 +385,11 @@ describe('MetaMask', function () {
await configureGas.click()
await delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
await delay(regularDelayMs)
// Continue to next screen
@ -365,19 +408,20 @@ describe('MetaMask', function () {
const transactions = await findElements(driver, By.css('.tx-list-item'))
assert.equal(transactions.length, 1)
const txValues = await findElements(driver, By.css('.tx-list-value'))
assert.equal(txValues.length, 1)
assert.equal(await txValues[0].getText(), '1 ETH')
const txValues = await findElement(driver, By.css('.tx-list-value'))
await driver.wait(until.elementTextMatches(txValues, /1\sETH/), 10000)
})
})
describe('Send ETH from Faucet', () => {
it('starts a send transaction inside Faucet', async () => {
await driver.executeScript('window.open("https://faucet.metamask.io")')
await delay(waitingNewPageDelayMs)
await openNewPage(driver, 'https://faucet.metamask.io')
const [extension, faucet] = await driver.getAllWindowHandles()
await driver.switchTo().window(faucet)
const faucetPageTitle = await findElement(driver, By.css('.container-fluid'))
await driver.wait(until.elementTextMatches(faucetPageTitle, /MetaMask/))
await delay(regularDelayMs)
const send1eth = await findElement(driver, By.xpath(`//button[contains(text(), '10 ether')]`), 14000)
@ -403,47 +447,133 @@ describe('MetaMask', function () {
})
})
describe('Add existing token using search', () => {
it('clicks on the Add Token button', async () => {
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
await addToken.click()
describe('Deploy contract and call contract methods', () => {
let extension
let contractTestPage
it('confirms a deploy contract transaction', async () => {
await openNewPage(driver, 'http://127.0.0.1:8080/');
[extension, contractTestPage] = await driver.getAllWindowHandles()
await delay(regularDelayMs)
const deployContractButton = await findElement(driver, By.css('#deployButton'))
await deployContractButton.click()
await delay(regularDelayMs)
await driver.switchTo().window(extension)
await delay(regularDelayMs)
const txListItem = await findElement(driver, By.css('.tx-list-item'))
await txListItem.click()
await delay(regularDelayMs)
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
const txStatuses = await findElements(driver, By.css('.tx-list-status'))
await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
const txAccounts = await findElements(driver, By.css('.tx-list-account'))
assert.equal(await txAccounts[0].getText(), 'Contract Deployment')
})
it('can pick a token from the existing options', async () => {
const tokenSearch = await findElement(driver, By.css('#search-tokens'))
await tokenSearch.sendKeys('BAT')
it('calls and confirms a contract method where ETH is sent', async () => {
await driver.switchTo().window(contractTestPage)
await delay(regularDelayMs)
const token = await findElement(driver, By.xpath("//span[contains(text(), 'BAT')]"))
await token.click()
const depositButton = await findElement(driver, By.css('#depositButton'))
await depositButton.click()
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await driver.switchTo().window(extension)
await delay(regularDelayMs)
const addTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Tokens')]`))
await addTokens.click()
await delay(largeDelayMs)
const txListItem = await findElement(driver, By.css('.tx-list-item'))
await txListItem.click()
await delay(regularDelayMs)
// Set the gas limit
const configureGas = await findElement(driver, By.css('.sliders-icon-container'))
await configureGas.click()
await delay(regularDelayMs)
const gasModal = await driver.findElement(By.css('span .modal'))
await driver.wait(until.elementLocated(By.css('.send-v2__customize-gas__title')))
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.customize-gas-input'))
await gasPriceInput.clear()
await gasPriceInput.sendKeys('10')
await gasLimitInput.clear()
await gasLimitInput.sendKeys('60001')
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await delay(regularDelayMs)
await driver.wait(until.stalenessOf(gasModal))
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
const txStatuses = await findElements(driver, By.css('.tx-list-status'))
await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
const txValues = await findElement(driver, By.css('.tx-list-value'))
await driver.wait(until.elementTextMatches(txValues, /3\sETH/), 10000)
const txAccounts = await findElements(driver, By.css('.tx-list-account'))
const firstTxAddress = await txAccounts[0].getText()
assert(firstTxAddress.match(/^0x\w{8}\.{3}\w{4}$/))
})
it('renders the balance for the chosen token', async () => {
it('calls and confirms a contract method where ETH is received', async () => {
await driver.switchTo().window(contractTestPage)
await delay(regularDelayMs)
const withdrawButton = await findElement(driver, By.css('#withdrawButton'))
await withdrawButton.click()
await delay(regularDelayMs)
await driver.switchTo().window(extension)
await delay(regularDelayMs)
const txListItem = await findElement(driver, By.css('.tx-list-item'))
await txListItem.click()
await delay(regularDelayMs)
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
const txStatuses = await findElements(driver, By.css('.tx-list-status'))
await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
const txValues = await findElement(driver, By.css('.tx-list-value'))
await driver.wait(until.elementTextMatches(txValues, /0\sETH/), 10000)
await driver.switchTo().window(contractTestPage)
await driver.close()
await driver.switchTo().window(extension)
})
it('renders the correct ETH balance', async () => {
const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /^0\s*BAT\s*$/), 10000)
await driver.wait(until.elementTextMatches(balance, /^86.*ETH.*$/), 10000)
const tokenAmount = await balance.getText()
assert.ok(/^0\s*BAT\s*$/.test(tokenAmount))
assert.ok(/^86.*ETH.*$/.test(tokenAmount))
await delay(regularDelayMs)
})
})
describe('Add a custom token from TokenFactory', () => {
it('creates a new token', async () => {
await driver.executeScript('window.open("https://tokenfactory.surge.sh/#/factory")')
await delay(waitingNewPageDelayMs)
openNewPage(driver, 'https://tokenfactory.surge.sh/#/factory')
await delay(regularDelayMs * 10)
const [extension, tokenFactory] = await driver.getAllWindowHandles()
await driver.switchTo().window(tokenFactory)
const [
totalSupply,
tokenName,
@ -470,12 +600,16 @@ describe('MetaMask', function () {
await driver.switchTo().window(tokenFactory)
await delay(regularDelayMs)
const tokenContactAddress = await driver.findElement(By.css('div > div > div:nth-child(2) > span:nth-child(3)'))
tokenAddress = await tokenContactAddress.getText()
await driver.close()
await driver.switchTo().window(extension)
await loadExtension(driver, extensionId)
await driver.switchTo().window(extension)
await delay(regularDelayMs)
})
it('clicks on the Add Token button', async () => {
@ -510,4 +644,212 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})
})
describe('Send token from inside MetaMask', () => {
let gasModal
it('starts to send a transaction', async function () {
const sendButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Send')]`))
await sendButton.click()
await delay(regularDelayMs)
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
await inputAmount.sendKeys('50')
// Set the gas limit
const configureGas = await findElement(driver, By.css('.send-v2__gas-fee-display button'))
await configureGas.click()
await delay(regularDelayMs)
gasModal = await driver.findElement(By.css('span .modal'))
})
it('customizes gas', async () => {
await driver.wait(until.elementLocated(By.css('.send-v2__customize-gas__title')))
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
await save.click()
await delay(regularDelayMs)
})
it('transitions to the confirm screen', async () => {
await driver.wait(until.stalenessOf(gasModal))
// Continue to next screen
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
})
it('submits the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
})
it('finds the transaction in the transactions list', async function () {
const transactions = await findElements(driver, By.css('.tx-list-item'))
assert.equal(transactions.length, 1)
const txValues = await findElements(driver, By.css('.tx-list-value'))
assert.equal(txValues.length, 1)
// test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved,
// or possibly until we use latest version of firefox in the tests
if (process.env.SELENIUM_BROWSER !== 'firefox') {
await driver.wait(until.elementTextMatches(txValues[0], /50\sTST/), 10000)
}
const txStatuses = await findElements(driver, By.css('.tx-list-status'))
const tx = await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed|Failed/), 10000)
assert.equal(await tx.getText(), 'Confirmed')
})
})
describe('Send a custom token from TokenFactory', () => {
let gasModal
it('sends an already created token', async () => {
openNewPage(driver, `https://tokenfactory.surge.sh/#/token/${tokenAddress}`)
const [extension] = await driver.getAllWindowHandles()
const [
transferToAddress,
transferToAmount,
] = await findElements(driver, By.css('.form-control'))
await transferToAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
await transferToAmount.sendKeys('26')
const transferAmountButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Transfer Amount')]`))
await transferAmountButton.click()
await delay(regularDelayMs)
const [,, popup] = await driver.getAllWindowHandles()
await driver.switchTo().window(popup)
await driver.close()
await driver.switchTo().window(extension)
await delay(regularDelayMs)
const [txListItem] = await findElements(driver, By.css('.tx-list-item'))
await txListItem.click()
await delay(regularDelayMs)
// Set the gas limit
const configureGas = await driver.wait(until.elementLocated(By.css('.send-v2__gas-fee-display button')))
await configureGas.click()
await delay(regularDelayMs)
gasModal = await driver.findElement(By.css('span .modal'))
})
it('customizes gas', async () => {
await driver.wait(until.elementLocated(By.css('.send-v2__customize-gas__title')))
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.customize-gas-input'))
await gasPriceInput.clear()
await delay(tinyDelayMs)
await gasPriceInput.sendKeys('10')
await delay(tinyDelayMs)
await gasLimitInput.clear()
await delay(tinyDelayMs)
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
await gasLimitInput.sendKeys('60000')
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'e'))
// Needed for different behaviour of input in different versions of firefox
const gasLimitInputValue = await gasLimitInput.getAttribute('value')
if (gasLimitInputValue === '600001') {
await gasLimitInput.sendKeys(Key.BACK_SPACE)
}
const save = await findElement(driver, By.css('.send-v2__customize-gas__save'))
await save.click()
await driver.wait(until.stalenessOf(gasModal))
const gasFeeInput = await findElement(driver, By.css('.currency-display__input'))
assert.equal(await gasFeeInput.getAttribute('value'), 0.0006)
})
it('submits the transaction', async function () {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
await delay(regularDelayMs)
})
it('finds the transaction in the transactions list', async function () {
const transactions = await findElements(driver, By.css('.tx-list-item'))
assert.equal(transactions.length, 2)
const txValues = await findElements(driver, By.css('.tx-list-value'))
await driver.wait(until.elementTextMatches(txValues[0], /26\sTST/))
const txStatuses = await findElements(driver, By.css('.tx-list-status'))
await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
const walletBalance = await findElement(driver, By.css('.wallet-balance'))
await walletBalance.click()
const tokenListItems = await findElements(driver, By.css('.token-list-item'))
await tokenListItems[0].click()
// test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved,
// or possibly until we use latest version of firefox in the tests
if (process.env.SELENIUM_BROWSER !== 'firefox') {
const tokenBalanceAmount = await findElement(driver, By.css('.token-balance__amount'))
assert.equal(await tokenBalanceAmount.getText(), '24')
}
})
})
describe('Hide token', () => {
it('hides the token when clicked', async () => {
const [hideTokenEllipsis] = await findElements(driver, By.css('.token-list-item__ellipsis'))
await hideTokenEllipsis.click()
const byTokenMenuDropdownOption = By.css('.menu__item--clickable')
const tokenMenuDropdownOption = await driver.wait(until.elementLocated(byTokenMenuDropdownOption))
await tokenMenuDropdownOption.click()
const confirmHideModal = await findElement(driver, By.css('span .modal'))
const byHideTokenConfirmationButton = By.css('.hide-token-confirmation__button')
const hideTokenConfirmationButton = await driver.wait(until.elementLocated(byHideTokenConfirmationButton))
await hideTokenConfirmationButton.click()
await driver.wait(until.stalenessOf(confirmHideModal))
})
})
describe('Add existing token using search', () => {
it('clicks on the Add Token button', async () => {
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
await addToken.click()
await delay(regularDelayMs)
})
it('can pick a token from the existing options', async () => {
const tokenSearch = await findElement(driver, By.css('#search-tokens'))
await tokenSearch.sendKeys('BAT')
await delay(regularDelayMs)
const token = await findElement(driver, By.xpath("//span[contains(text(), 'BAT')]"))
await token.click()
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.xpath(`//button[contains(text(), 'Next')]`))
await nextScreen.click()
await delay(regularDelayMs)
const addTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Tokens')]`))
await addTokens.click()
await delay(largeDelayMs)
})
it('renders the balance for the chosen token', async () => {
const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /0\sBAT/))
await delay(regularDelayMs)
})
})
})

@ -6,5 +6,5 @@ set -o pipefail
export PATH="$PATH:./node_modules/.bin"
shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'

@ -4,7 +4,7 @@ const path = require('path')
const assert = require('assert')
const pify = require('pify')
const webdriver = require('selenium-webdriver')
const { By, Key } = webdriver
const { By, Key, until } = webdriver
const { delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('./func')
describe('Metamask popup page', function () {
@ -229,7 +229,11 @@ describe('Metamask popup page', function () {
it('confirms transaction', async function () {
await delay(300)
await driver.findElement(By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input')).click()
const bySubmitButton = By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input')
const submitButton = await driver.wait(until.elementLocated(bySubmitButton))
submitButton.click()
await delay(500)
})
@ -269,7 +273,8 @@ describe('Metamask popup page', function () {
it('confirms transaction in MetaMask popup', async function () {
const windowHandles = await driver.getAllWindowHandles()
await driver.switchTo().window(windowHandles[windowHandles.length - 1])
const metamaskSubmit = await driver.findElement(By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input'))
const byMetamaskSubmit = By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input')
const metamaskSubmit = await driver.wait(until.elementLocated(byMetamaskSubmit))
await metamaskSubmit.click()
await delay(1000)
})

@ -75,7 +75,7 @@ async function runAddTokenFlowTest (assert, done) {
tokenWrapper[0].click()
// Click Next button
let nextButton = await queryAsync($, 'button.btn-primary.btn--large')
const nextButton = await queryAsync($, 'button.btn-primary.btn--large')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()

@ -1,10 +1,7 @@
const reactTriggerChange = require('react-trigger-change')
const {
timeout,
queryAsync,
findAsync,
} = require('../../lib/util')
const PASSWORD = 'password123'
QUnit.module('confirm sig requests')
@ -17,7 +14,7 @@ QUnit.test('successful confirmation of sig requests', (assert) => {
})
async function runConfirmSigRequestsTest (assert, done) {
let selectState = await queryAsync($, 'select')
const selectState = await queryAsync($, 'select')
selectState.val('confirm sig requests')
reactTriggerChange(selectState[0])
@ -32,7 +29,7 @@ async function runConfirmSigRequestsTest(assert, done) {
let confirmSigHeadline = await queryAsync($, '.request-signature__headline')
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested')
let confirmSigMessage = await queryAsync($, '.request-signature__notice')
const confirmSigMessage = await queryAsync($, '.request-signature__notice')
assert.ok(confirmSigMessage[0].textContent.match(/^Signing\sthis\smessage/))
let confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
@ -45,7 +42,7 @@ async function runConfirmSigRequestsTest(assert, done) {
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested')
confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/))
assert.ok(confirmSigRowValue[0].textContent.match(/^#\sTerms\sof\sUse/))
confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
confirmSigSignButton[0].click()

@ -42,7 +42,7 @@ async function runFirstTimeUsageTest (assert, done) {
assert.equal(created.textContent, 'Your unique account image', 'unique image screen')
// Agree button
let button = (await findAsync(app, 'button'))[0]
const button = (await findAsync(app, 'button'))[0]
assert.ok(button, 'button present')
button.click()

@ -5,8 +5,6 @@ const {
findAsync,
} = require('../../lib/util')
const PASSWORD = 'password123'
QUnit.module('new ui send flow')
QUnit.test('successful send flow', (assert) => {
@ -87,7 +85,7 @@ async function runSendFlowTest(assert, done) {
sendFromFieldItemAddress = await queryAsync($, '.account-list-item__account-name')
assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name')
let sendToFieldInput = await queryAsync($, '.send-v2__to-autocomplete__input')
const sendToFieldInput = await queryAsync($, '.send-v2__to-autocomplete__input')
sendToFieldInput[0].focus()
const sendToDropdownList = await queryAsync($, '.send-v2__from-dropdown__list')

@ -1,6 +1,5 @@
const reactTriggerChange = require('../../lib/react-trigger-change')
const {
timeout,
queryAsync,
findAsync,
} = require('../../lib/util')

@ -31,6 +31,6 @@ module.exports = {
getRandomValues () {
return 'SOO RANDO!!!1'
}
},
}

@ -17,14 +17,14 @@ class TxGenerator {
}
generate (tx = {}, opts = {}) {
let { count, fromNonce } = opts
const { count, fromNonce } = opts
let nonce = fromNonce || this.txs.length
let txs = []
const txs = []
for (let i = 0; i < count; i++) {
txs.push(extend(template, {
txParams: {
nonce: hexify(nonce++),
}
},
}, tx))
}
this.txs = this.txs.concat(txs)

@ -13,7 +13,7 @@
/* our tests can pass in chrome and firefox. */
/** ************************************************/
'use strict';
'use strict'
// Constants and functions are declared inside the closure.
// In this way, reactTriggerChange can be passed directly to executeScript in Selenium.
@ -33,47 +33,47 @@ module.exports = function reactTriggerChange(node) {
text: true,
time: true,
url: true,
week: true
};
var nodeName = node.nodeName.toLowerCase();
var type = node.type;
var event;
var descriptor;
var initialValue;
var initialChecked;
var initialCheckedRadio;
week: true,
}
var nodeName = node.nodeName.toLowerCase()
var type = node.type
var event
var descriptor
var initialValue
var initialChecked
var initialCheckedRadio
// Do not try to delete non-configurable properties.
// Value and checked properties on DOM elements are non-configurable in PhantomJS.
function deletePropertySafe (elem, prop) {
var desc = Object.getOwnPropertyDescriptor(elem, prop);
var desc = Object.getOwnPropertyDescriptor(elem, prop)
if (desc && desc.configurable) {
delete elem[prop];
delete elem[prop]
}
}
function getCheckedRadio (radio) {
var name = radio.name;
var radios;
var i;
var name = radio.name
var radios
var i
if (name) {
radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]');
radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]')
for (i = 0; i < radios.length; i += 1) {
if (radios[i].checked) {
return radios[i] !== radio ? radios[i] : null;
return radios[i] !== radio ? radios[i] : null
}
}
}
return null;
return null
}
function preventChecking (e) {
e.preventDefault();
e.preventDefault()
if (!initialChecked) {
e.target.checked = false;
e.target.checked = false
}
if (initialCheckedRadio) {
initialCheckedRadio.checked = true;
initialCheckedRadio.checked = true
}
}
@ -81,81 +81,81 @@ module.exports = function reactTriggerChange(node) {
(nodeName === 'input' && type === 'file')) {
// IE9-IE11, non-IE
// Dispatch change.
event = document.createEvent('HTMLEvents');
event.initEvent('change', true, false);
node.dispatchEvent(event);
event = document.createEvent('HTMLEvents')
event.initEvent('change', true, false)
node.dispatchEvent(event)
} else if ((nodeName === 'input' && supportedInputTypes[type]) ||
nodeName === 'textarea') {
// React 16
// Cache artificial value property descriptor.
// Property doesn't exist in React <16, descriptor is undefined.
descriptor = Object.getOwnPropertyDescriptor(node, 'value');
descriptor = Object.getOwnPropertyDescriptor(node, 'value')
// Update inputValueTracking cached value.
// Remove artificial value property.
// Restore initial value to trigger event with it.
initialValue = node.value;
node.value = initialValue + '#';
deletePropertySafe(node, 'value');
node.value = initialValue;
initialValue = node.value
node.value = initialValue + '#'
deletePropertySafe(node, 'value')
node.value = initialValue
// React 0.14: IE10-IE11, non-IE
// React 15: non-IE
// React 16: IE10-IE11, non-IE
event = document.createEvent('HTMLEvents');
event.initEvent('input', true, false);
node.dispatchEvent(event);
event = document.createEvent('HTMLEvents')
event.initEvent('input', true, false)
node.dispatchEvent(event)
// React 16
// Restore artificial value property descriptor.
if (descriptor) {
Object.defineProperty(node, 'value', descriptor);
Object.defineProperty(node, 'value', descriptor)
}
} else if (nodeName === 'input' && type === 'checkbox') {
// Invert inputValueTracking cached value.
node.checked = !node.checked;
node.checked = !node.checked
// Dispatch click.
// Click event inverts checked value.
event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
node.dispatchEvent(event);
event = document.createEvent('MouseEvents')
event.initEvent('click', true, true)
node.dispatchEvent(event)
} else if (nodeName === 'input' && type === 'radio') {
// Cache initial checked value.
initialChecked = node.checked;
initialChecked = node.checked
// Find and cache initially checked radio in the group.
initialCheckedRadio = getCheckedRadio(node);
initialCheckedRadio = getCheckedRadio(node)
// React 16
// Cache property descriptor.
// Invert inputValueTracking cached value.
// Remove artificial checked property.
// Restore initial value, otherwise preventDefault will eventually revert the value.
descriptor = Object.getOwnPropertyDescriptor(node, 'checked');
node.checked = !initialChecked;
deletePropertySafe(node, 'checked');
node.checked = initialChecked;
descriptor = Object.getOwnPropertyDescriptor(node, 'checked')
node.checked = !initialChecked
deletePropertySafe(node, 'checked')
node.checked = initialChecked
// Prevent toggling during event capturing phase.
// Set checked value to false if initialChecked is false,
// otherwise next listeners will see true.
// Restore initially checked radio in the group.
node.addEventListener('click', preventChecking, true);
node.addEventListener('click', preventChecking, true)
// Dispatch click.
// Click event inverts checked value.
event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
node.dispatchEvent(event);
event = document.createEvent('MouseEvents')
event.initEvent('click', true, true)
node.dispatchEvent(event)
// Remove listener to stop further change prevention.
node.removeEventListener('click', preventChecking, true);
node.removeEventListener('click', preventChecking, true)
// React 16
// Restore artificial checked property descriptor.
if (descriptor) {
Object.defineProperty(node, 'checked', descriptor);
Object.defineProperty(node, 'checked', descriptor)
}
}
}
};

@ -7,7 +7,7 @@ module.exports = function(config) {
settings.files.push('dist/mascara/ui.js')
settings.files.push('dist/mascara/tests.js')
// service worker background
settings.files.push({ pattern: 'dist/mascara/background.js', watched: false, included: false, served: true }),
settings.files.push({ pattern: 'dist/mascara/background.js', watched: false, included: false, served: true })
settings.proxies['/background.js'] = '/base/dist/mascara/background.js'
// use this to keep the browser open for debugging

@ -12,7 +12,7 @@ const pngFileStream = require('png-file-stream')
const sizeOfPng = require('image-size/lib/types/png')
const By = webdriver.By
const localesIndex = require('../../app/_locales/index.json')
const { delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('../e2e/func')
const { delay, buildChromeWebDriver, getExtensionIdChrome } = require('../e2e/func')
const eth = new Ethjs(new Ethjs.HttpProvider('http://localhost:8545'))
@ -43,9 +43,7 @@ captureAllScreens()
async function captureAllScreens () {
// common names
let button
let tabs
let element
await cleanScreenShotDir()
@ -108,7 +106,7 @@ async function captureAllScreens() {
await captureLanguageScreenShots('terms')
await delay(300)
element = driver.findElement(By.linkText('Attributions'))
const element = driver.findElement(By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', element)
await delay(300)
await captureLanguageScreenShots('terms-scrolled')
@ -134,7 +132,7 @@ async function captureAllScreens() {
// enter seed phrase
const seedPhraseButtons = await driver.findElements(By.css('.backup-phrase__confirm-seed-options > button'))
const seedPhraseButtonWords = await Promise.all(seedPhraseButtons.map(button => button.getText()))
for (let targetWord of seedPhraseWords) {
for (const targetWord of seedPhraseWords) {
const wordIndex = seedPhraseButtonWords.indexOf(targetWord)
if (wordIndex === -1) throw new Error(`Captured seed phrase word "${targetWord}" not in found seed phrase button options ${seedPhraseButtonWords.join(' ')}`)
await driver.findElement(By.css(`.backup-phrase__confirm-seed-options > button:nth-child(${wordIndex + 1})`)).click()
@ -195,7 +193,7 @@ async function captureLanguageScreenShots(label) {
const nonEnglishLocales = localesIndex.filter(localeMeta => localeMeta.code !== 'en')
// take english shot
await captureScreenShot(`${label} (en)`)
for (let localeMeta of nonEnglishLocales) {
for (const localeMeta of nonEnglishLocales) {
// set locale and take shot
await setLocale(localeMeta.code)
await delay(300)

@ -1,5 +1,4 @@
const assert = require('assert')
const path = require('path')
const ethUtil = require('ethereumjs-util')
const accountImporter = require('../../../app/scripts/account-import-strategies/index')
const { assertRejects } = require('../test-utils')

@ -12,7 +12,7 @@ const stubPreferencesStore = {
},
}
},
};
}
describe('address-book-controller', function () {
var addressBookController

@ -5,9 +5,6 @@ const {
getNetworkDisplayName,
} = require('../../../../app/scripts/controllers/network/util')
const { createTestProviderTools } = require('../../../stub/provider')
const providerResultStub = {}
describe('# Network Controller', function () {
let networkController
const noop = () => {}

@ -1,12 +1,10 @@
const assert = require('assert')
const NonceTracker = require('../../../../../app/scripts/controllers/transactions/nonce-tracker')
const MockTxGen = require('../../../../lib/mock-tx-gen')
let providerResultStub = {}
const providerResultStub = {}
describe('Nonce Tracker', function () {
let nonceTracker, provider
let getPendingTransactions, pendingTxs
let getConfirmedTransactions, confirmedTxs
let nonceTracker, pendingTxs, confirmedTxs
describe('#getNonceLock', function () {
@ -182,8 +180,8 @@ describe('Nonce Tracker', function () {
describe('When all three return different values', function () {
beforeEach(function () {
const txGen = new MockTxGen()
const confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 })
const pendingTxs = txGen.generate({
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 })
pendingTxs = txGen.generate({
status: 'submitted',
nonce: 100,
}, { count: 1 })
@ -202,8 +200,8 @@ describe('Nonce Tracker', function () {
describe('Faq issue 67', function () {
beforeEach(function () {
const txGen = new MockTxGen()
const confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
const pendingTxs = txGen.generate({
confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
pendingTxs = txGen.generate({
status: 'submitted',
}, { count: 10 })
// 0x40 is 64 in hex:

@ -1,20 +1,12 @@
const assert = require('assert')
const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx')
const ObservableStore = require('obs-store')
const clone = require('clone')
const { createTestProviderTools } = require('../../../../stub/provider')
const PendingTransactionTracker = require('../../../../../app/scripts/controllers/transactions/pending-tx-tracker')
const MockTxGen = require('../../../../lib/mock-tx-gen')
const sinon = require('sinon')
const noop = () => true
const currentNetworkId = 42
const otherNetworkId = 36
const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
describe('PendingTransactionTracker', function () {
let pendingTxTracker, txMeta, txMetaNoHash, txMetaNoRawTx, providerResultStub,
let pendingTxTracker, txMeta, txMetaNoHash, providerResultStub,
provider, txMeta3, txList, knownErrors
this.timeout(10000)
beforeEach(function () {
@ -34,11 +26,7 @@ describe('PendingTransactionTracker', function () {
status: 'signed',
txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'},
}
txMetaNoRawTx = {
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: 'signed',
txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'},
}
providerResultStub = {}
provider = createTestProviderTools({ scaffold: providerResultStub }).provider
@ -47,7 +35,7 @@ describe('PendingTransactionTracker', function () {
nonceTracker: {
getGlobalLock: async () => {
return { releaseLock: () => {} }
}
},
},
getPendingTransactions: () => { return [] },
getCompletedTransactions: () => { return [] },
@ -133,22 +121,20 @@ describe('PendingTransactionTracker', function () {
})
describe('#queryPendingTxs', function () {
it('should call #_checkPendingTxs if their is no oldBlock', function (done) {
let newBlock, oldBlock
newBlock = { number: '0x01' }
let oldBlock
const newBlock = { number: '0x01' }
pendingTxTracker._checkPendingTxs = done
pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
})
it('should call #_checkPendingTxs if oldBlock and the newBlock have a diff of greater then 1', function (done) {
let newBlock, oldBlock
oldBlock = { number: '0x01' }
newBlock = { number: '0x03' }
const oldBlock = { number: '0x01' }
const newBlock = { number: '0x03' }
pendingTxTracker._checkPendingTxs = done
pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
})
it('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less', function (done) {
let newBlock, oldBlock
oldBlock = { number: '0x1' }
newBlock = { number: '0x2' }
const oldBlock = { number: '0x1' }
const newBlock = { number: '0x2' }
pendingTxTracker._checkPendingTxs = () => {
const err = new Error('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less')
done(err)
@ -197,7 +183,6 @@ describe('PendingTransactionTracker', function () {
it('should warp all txMeta\'s in #_checkPendingTx', function (done) {
pendingTxTracker.getPendingTransactions = () => txList
pendingTxTracker._checkPendingTx = (tx) => { tx.resolve(tx) }
const list = txList.map
Promise.all(txList.map((tx) => tx.processed))
.then((txCompletedList) => done())
.catch(done)
@ -207,7 +192,7 @@ describe('PendingTransactionTracker', function () {
})
describe('#resubmitPendingTxs', function () {
const blockStub = { number: '0x0' };
const blockStub = { number: '0x0' }
beforeEach(function () {
const txMeta2 = txMeta3 = txMeta
txList = [txMeta, txMeta2, txMeta3].map((tx) => {
@ -275,7 +260,7 @@ describe('PendingTransactionTracker', function () {
})
describe('#_resubmitTx', function () {
const mockFirstRetryBlockNumber = '0x1'
let txMetaToTestExponentialBackoff
let txMetaToTestExponentialBackoff, enoughBalance
beforeEach(() => {
pendingTxTracker.getBalance = (address) => {
@ -298,7 +283,7 @@ describe('PendingTransactionTracker', function () {
})
it('should publish the transaction', function (done) {
const enoughBalance = '0x100000'
enoughBalance = '0x100000'
// Stubbing out current account state:
// Adding the fake tx:
@ -313,7 +298,7 @@ describe('PendingTransactionTracker', function () {
})
it('should not publish the transaction if the limit of retries has been exceeded', function (done) {
const enoughBalance = '0x100000'
enoughBalance = '0x100000'
const mockLatestBlockNumber = '0x5'
pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
@ -327,7 +312,7 @@ describe('PendingTransactionTracker', function () {
})
it('should publish the transaction if the number of blocks since last retry exceeds the last set limit', function (done) {
const enoughBalance = '0x100000'
enoughBalance = '0x100000'
const mockLatestBlockNumber = '0x11'
pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
@ -343,7 +328,7 @@ describe('PendingTransactionTracker', function () {
describe('#_checkIfNonceIsTaken', function () {
beforeEach(function () {
let confirmedTxList = [{
const confirmedTxList = [{
id: 1,
hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
status: 'confirmed',

@ -28,7 +28,7 @@ describe('Recipient Blacklist Checker', function () {
it('does not fail on test networks', function () {
let callCount = 0
const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE]
for (let networkId in networks) {
for (const networkId in networks) {
publicAccounts.forEach((account) => {
recipientBlackListChecker.checkAccount(networkId, account)
callCount++

@ -1,20 +1,17 @@
const assert = require('assert')
const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx')
const EthjsQuery = require('ethjs-query')
const ObservableStore = require('obs-store')
const sinon = require('sinon')
const TransactionController = require('../../../../../app/scripts/controllers/transactions')
const TxGasUtils = require('../../../../../app/scripts/controllers/transactions/tx-gas-utils')
const { createTestProviderTools, getTestAccounts } = require('../../../../stub/provider')
const noop = () => true
const currentNetworkId = 42
const otherNetworkId = 36
describe('Transaction Controller', function () {
let txController, provider, providerResultStub, query, fromAccount
let txController, provider, providerResultStub, fromAccount
beforeEach(function () {
providerResultStub = {
@ -24,7 +21,6 @@ describe('Transaction Controller', function () {
eth_getCode: '0x',
}
provider = createTestProviderTools({ scaffold: providerResultStub }).provider
query = new EthjsQuery(provider)
fromAccount = getTestAccounts()[0]
txController = new TransactionController({
@ -388,7 +384,7 @@ describe('Transaction Controller', function () {
describe('#retryTransaction', function () {
it('should create a new txMeta with the same txParams as the original one', function (done) {
let txParams = {
const txParams = {
nonce: '0x00',
from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
to: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',

@ -1,6 +1,5 @@
const assert = require('assert')
const Transaction = require('ethereumjs-tx')
const BN = require('bn.js')
const { hexToBn, bnToHex } = require('../../../../../app/scripts/lib/util')

@ -9,9 +9,9 @@ describe ('Transaction state history helper', function () {
const input = {
foo: {
bar: {
bam: 'baz'
}
}
bam: 'baz',
},
},
}
const output = txStateHistoryHelper.snapshotFromTxMeta(input)
assert('foo' in output, 'has a foo key')
@ -50,14 +50,14 @@ describe ('Transaction state history helper', function () {
it('replaying history does not mutate the original obj', function () {
const initialState = { test: true, message: 'hello', value: 1 }
const diff1 = [{
"op": "replace",
"path": "/message",
"value": "haay",
'op': 'replace',
'path': '/message',
'value': 'haay',
}]
const diff2 = [{
"op": "replace",
"path": "/value",
"value": 2,
'op': 'replace',
'path': '/value',
'value': 2,
}]
const history = [initialState, diff1, diff2]
@ -78,9 +78,9 @@ describe ('Transaction state history helper', function () {
someValue: 'value 1',
foo: {
bar: {
bam: 'baz'
}
}
bam: 'baz',
},
},
}
const nextState = {
@ -89,9 +89,9 @@ describe ('Transaction state history helper', function () {
foo: {
newPropFirstLevel: 'new property - first level',
bar: {
bam: 'baz'
}
}
bam: 'baz',
},
},
}
const before = new Date().getTime()
@ -106,8 +106,7 @@ describe ('Transaction state history helper', function () {
assert.equal(result[0].path, expectedEntry1.path)
assert.equal(result[0].value, expectedEntry1.value)
assert.equal(result[0].value, expectedEntry1.value)
if (note)
assert.equal(result[0].note, note)
if (note) { assert.equal(result[0].note, note) }
assert.ok(result[0].timestamp >= before && result[0].timestamp <= after)

@ -1,6 +1,4 @@
const assert = require('assert')
const clone = require('clone')
const ObservableStore = require('obs-store')
const TxStateManager = require('../../../../../app/scripts/controllers/transactions/tx-state-manager')
const txStateHistoryHelper = require('../../../../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
const noop = () => true
@ -16,23 +14,23 @@ describe('TransactionStateManager', function () {
transactions: [],
},
txHistoryLimit: 10,
getNetwork: () => currentNetworkId
getNetwork: () => currentNetworkId,
})
})
describe('#setTxStatusSigned', function () {
it('sets the tx status to signed', function () {
let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
txStateManager.setTxStatusSigned(1)
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 1)
assert.equal(result[0].status, 'signed')
})
it('should emit a signed event to signal the exciton of callback', (done) => {
let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const noop = function () {
assert(true, 'event listener has been triggered and noop executed')
done()
@ -46,19 +44,22 @@ describe('TransactionStateManager', function () {
describe('#setTxStatusRejected', function () {
it('sets the tx status to rejected', function () {
let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx)
txStateManager.setTxStatusRejected(1)
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 1)
assert.equal(result[0].status, 'rejected')
})
it('should emit a rejected event to signal the exciton of callback', (done) => {
let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx)
const noop = function (err, txId) {
if (err) {
console.log('Error: ', err)
}
assert(true, 'event listener has been triggered and noop executed')
done()
}
@ -69,7 +70,7 @@ describe('TransactionStateManager', function () {
describe('#getFullTxList', function () {
it('when new should return empty array', function () {
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
@ -77,7 +78,7 @@ describe('TransactionStateManager', function () {
describe('#getTxList', function () {
it('when new should return empty array', function () {
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
@ -85,21 +86,21 @@ describe('TransactionStateManager', function () {
describe('#addTx', function () {
it('adds a tx returned in getTxList', function () {
let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 1)
assert.equal(result[0].id, 1)
})
it('does not override txs from other networks', function () {
let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
let tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
const tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
const tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
txStateManager.addTx(tx2, noop)
let result = txStateManager.getFullTxList()
let result2 = txStateManager.getTxList()
const result = txStateManager.getFullTxList()
const result2 = txStateManager.getTxList()
assert.equal(result.length, 2, 'txs were deleted')
assert.equal(result2.length, 1, 'incorrect number of txs on network.')
})
@ -110,7 +111,7 @@ describe('TransactionStateManager', function () {
const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
}
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
assert.equal(result[0].id, 1, 'early txs truncted')
})
@ -121,20 +122,20 @@ describe('TransactionStateManager', function () {
const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
}
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
assert.equal(result[0].id, 1, 'early txs truncted')
})
it('cuts off early txs beyond a limit but does not cut unapproved txs', function () {
let unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
const unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(unconfirmedTx, noop)
const limit = txStateManager.txHistoryLimit
for (let i = 1; i < limit + 1; i++) {
const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx, noop)
}
let result = txStateManager.getTxList()
const result = txStateManager.getTxList()
assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
assert.equal(result[0].id, 0, 'first tx should still be there')
assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
@ -149,7 +150,7 @@ describe('TransactionStateManager', function () {
const txMeta = txStateManager.getTx('1')
txMeta.hash = 'foo'
txStateManager.updateTx(txMeta)
let result = txStateManager.getTx('1')
const result = txStateManager.getTx('1')
assert.equal(result.hash, 'foo')
})
@ -166,8 +167,6 @@ describe('TransactionStateManager', function () {
},
}
const updatedMeta = clone(txMeta)
txStateManager.addTx(txMeta)
const updatedTx = txStateManager.getTx('1')
// verify tx was initialized correctly

@ -27,7 +27,7 @@ describe('txUtils', function () {
describe('#normalizeTxParams', () => {
it('should normalize txParams', () => {
let txParams = {
const txParams = {
chainId: '0x1',
from: 'a7df1beDBF813f57096dF77FCd515f0B3900e402',
to: null,

@ -11,7 +11,7 @@ global.crypto = global.crypto || {
array[i] = Math.random() * 100
}
return array
}
},
}
describe('EdgeEncryptor', function () {
@ -33,10 +33,10 @@ describe('EdgeEncryptor', function () {
it('should return proper format.', function (done) {
edgeEncryptor.encrypt(password, data)
.then(function (encryptedData) {
let encryptedObject = JSON.parse(encryptedData)
const encryptedObject = JSON.parse(encryptedData)
assert.ok(encryptedObject.data, 'there is no data')
assert.ok(encryptedObject.iv && encryptedObject.iv.length != 0, 'there is no iv')
assert.ok(encryptedObject.salt && encryptedObject.salt.length != 0, 'there is no salt')
assert.ok(encryptedObject.iv && encryptedObject.iv.length !== 0, 'there is no iv')
assert.ok(encryptedObject.salt && encryptedObject.salt.length !== 0, 'there is no salt')
done()
}).catch(function (err) {
done(err)

@ -13,8 +13,12 @@ describe('nodeify', function () {
it('should retain original context', function (done) {
var nodified = nodeify(obj.promiseFunc, obj)
nodified('baz', function (err, res) {
if (!err) {
assert.equal(res, 'barbaz')
done()
} else {
done(new Error(err.toString()))
}
})
})

@ -2,7 +2,6 @@ const assert = require('assert')
const PendingBalanceCalculator = require('../../../app/scripts/lib/pending-balance-calculator')
const MockTxGen = require('../../lib/mock-tx-gen')
const BN = require('ethereumjs-util').BN
let providerResultStub = {}
const zeroBn = new BN(0)
const etherBn = new BN(String(1e18))
@ -20,7 +19,7 @@ describe('PendingBalanceCalculator', function () {
value: ether,
gasPrice: '0x0',
gas: '0x0',
}
},
}, { count: 1 })
const balanceCalculator = generateBalanceCalcWith([], zeroBn)
@ -36,7 +35,7 @@ describe('PendingBalanceCalculator', function () {
value: '0x0',
gasPrice: '0x2',
gas: '0x3',
}
},
}, { count: 1 })
const balanceCalculator = generateBalanceCalcWith([], zeroBn)
@ -66,7 +65,7 @@ describe('PendingBalanceCalculator', function () {
value: ether,
gasPrice: '0x0',
gas: '0x0',
}
},
}, { count: 1 })
balanceCalculator = generateBalanceCalcWith(pendingTxs, etherBn)

@ -9,11 +9,10 @@ describe('SeedPhraseVerifier', function () {
describe('verifyAccounts', function () {
let password = 'passw0rd1'
let hdKeyTree = 'HD Key Tree'
const password = 'passw0rd1'
const hdKeyTree = 'HD Key Tree'
let keyringController
let vault
let primaryKeyring
beforeEach(async function () {
@ -24,60 +23,60 @@ describe('SeedPhraseVerifier', function () {
assert(keyringController)
vault = await keyringController.createNewVaultAndKeychain(password)
await keyringController.createNewVaultAndKeychain(password)
primaryKeyring = keyringController.getKeyringsByType(hdKeyTree)[0]
})
it('should be able to verify created account with seed words', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let serialized = await primaryKeyring.serialize()
let seedWords = serialized.mnemonic
const serialized = await primaryKeyring.serialize()
const seedWords = serialized.mnemonic
assert.notEqual(seedWords.length, 0)
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
})
it('should be able to verify created account (upper case) with seed words', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let upperCaseAccounts = [createdAccounts[0].toUpperCase()]
const upperCaseAccounts = [createdAccounts[0].toUpperCase()]
let serialized = await primaryKeyring.serialize()
let seedWords = serialized.mnemonic
const serialized = await primaryKeyring.serialize()
const seedWords = serialized.mnemonic
assert.notEqual(seedWords.length, 0)
let result = await seedPhraseVerifier.verifyAccounts(upperCaseAccounts, seedWords)
await seedPhraseVerifier.verifyAccounts(upperCaseAccounts, seedWords)
})
it('should be able to verify created account (lower case) with seed words', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let lowerCaseAccounts = [createdAccounts[0].toLowerCase()]
const lowerCaseAccounts = [createdAccounts[0].toLowerCase()]
let serialized = await primaryKeyring.serialize()
let seedWords = serialized.mnemonic
const serialized = await primaryKeyring.serialize()
const seedWords = serialized.mnemonic
assert.notEqual(seedWords.length, 0)
let result = await seedPhraseVerifier.verifyAccounts(lowerCaseAccounts, seedWords)
await seedPhraseVerifier.verifyAccounts(lowerCaseAccounts, seedWords)
})
it('should return error with good but different seed words', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let serialized = await primaryKeyring.serialize()
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
await primaryKeyring.serialize()
const seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
try {
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
assert.fail("Should reject")
await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
assert.fail('Should reject')
} catch (err) {
assert.ok(err.message.indexOf('Not identical accounts!') >= 0, 'Wrong error message')
}
@ -85,15 +84,15 @@ describe('SeedPhraseVerifier', function () {
it('should return error with undefined existing accounts', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let serialized = await primaryKeyring.serialize()
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
await primaryKeyring.serialize()
const seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
try {
let result = await seedPhraseVerifier.verifyAccounts(undefined, seedWords)
assert.fail("Should reject")
await seedPhraseVerifier.verifyAccounts(undefined, seedWords)
assert.fail('Should reject')
} catch (err) {
assert.equal(err.message, 'No created accounts defined.')
}
@ -101,15 +100,15 @@ describe('SeedPhraseVerifier', function () {
it('should return error with empty accounts array', async function () {
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 1)
let serialized = await primaryKeyring.serialize()
let seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
await primaryKeyring.serialize()
const seedWords = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
try {
let result = await seedPhraseVerifier.verifyAccounts([], seedWords)
assert.fail("Should reject")
await seedPhraseVerifier.verifyAccounts([], seedWords)
assert.fail('Should reject')
} catch (err) {
assert.equal(err.message, 'No created accounts defined.')
}
@ -117,17 +116,17 @@ describe('SeedPhraseVerifier', function () {
it('should be able to verify more than one created account with seed words', async function () {
const keyState = await keyringController.addNewAccount(primaryKeyring)
const keyState2 = await keyringController.addNewAccount(primaryKeyring)
await keyringController.addNewAccount(primaryKeyring)
await keyringController.addNewAccount(primaryKeyring)
let createdAccounts = await primaryKeyring.getAccounts()
const createdAccounts = await primaryKeyring.getAccounts()
assert.equal(createdAccounts.length, 3)
let serialized = await primaryKeyring.serialize()
let seedWords = serialized.mnemonic
const serialized = await primaryKeyring.serialize()
const seedWords = serialized.mnemonic
assert.notEqual(seedWords.length, 0)
let result = await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
await seedPhraseVerifier.verifyAccounts(createdAccounts, seedWords)
})
})
})

@ -8,7 +8,7 @@ const mockState = {
accounts: { abc: {} },
network: 1,
selectedAddress: 'abc',
}
},
}
describe('BalanceComponent', function () {

@ -52,13 +52,13 @@ describe('BnInput', function () {
it('can tolerate wei precision', function (done) {
const renderer = ReactTestUtils.createRenderer()
let valueStr = '1000000000'
const valueStr = '1000000000'
const value = new BN(valueStr, 10)
const inputStr = '1.000000001'
let targetStr = '1000000001'
const targetStr = '1000000001'
const target = new BN(targetStr, 10)

@ -13,7 +13,7 @@ const mockState = {
identities,
conversionRate: 10,
selectedAddress: 'abc',
}
},
}
describe('PendingTx', function () {

@ -2,14 +2,14 @@ const assert = require('assert')
const migration22 = require('../../../app/scripts/migrations/022')
const properTime = (new Date()).getTime()
const storage = {
"meta": {},
"data": {
"TransactionController": {
"transactions": [
{ "status": "submitted" },
{ "status": "submitted", "submittedTime": properTime },
{"status": "confirmed"},
]
'meta': {},
'data': {
'TransactionController': {
'transactions': [
{ 'status': 'submitted' },
{ 'status': 'submitted', 'submittedTime': properTime },
{'status': 'confirmed'},
],
},
},
}

@ -1,12 +1,11 @@
const assert = require('assert')
const migration23 = require('../../../app/scripts/migrations/023')
const properTime = (new Date()).getTime()
const storage = {
"meta": {},
"data": {
"TransactionController": {
"transactions": [
]
'meta': {},
'data': {
'TransactionController': {
'transactions': [
],
},
},
}
@ -53,7 +52,6 @@ while (transactions20.length < 20) {
}
storage.data.TransactionController.transactions = transactions
describe('storage is migrated successfully and the proper transactions are remove from state', () => {

@ -4,13 +4,12 @@ const firstTimeState = {
meta: {},
data: require('../../../app/scripts/first-time-state'),
}
const properTime = (new Date()).getTime()
const storage = {
"meta": {},
"data": {
"TransactionController": {
"transactions": [
]
'meta': {},
'data': {
'TransactionController': {
'transactions': [
],
},
},
}

@ -6,11 +6,11 @@ const firstTimeState = {
}
const storage = {
"meta": {},
"data": {
"TransactionController": {
"transactions": [
]
'meta': {},
'data': {
'TransactionController': {
'transactions': [
],
},
},
}

@ -1,6 +1,5 @@
const assert = require('assert')
const migrationTemplate = require('../../../app/scripts/migrations/template')
const properTime = (new Date()).getTime()
const storage = {
meta: {},
data: {},

@ -1,24 +1,24 @@
const assert = require('assert');
const assert = require('assert')
const h = require('react-hyperscript');
const sinon = require('sinon');
const path = require('path');
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown;
const h = require('react-hyperscript')
const sinon = require('sinon')
const path = require('path')
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown
const { createMockStore } = require('redux-test-utils')
const { mountWithStore } = require('../../../lib/shallow-with-store')
const mockState = {
metamask: {
}
},
}
describe('Dropdown components', function () {
let onClickOutside;
let closeMenu;
let onClick;
let onClickOutside
let closeMenu
let onClick
let dropdownComponentProps = {
const dropdownComponentProps = {
isOpen: true,
zIndex: 11,
onClickOutside,
@ -34,9 +34,9 @@ describe('Dropdown components', function () {
let store
let component
beforeEach(function () {
onClickOutside = sinon.spy();
closeMenu = sinon.spy();
onClick = sinon.spy();
onClickOutside = sinon.spy()
closeMenu = sinon.spy()
onClick = sinon.spy()
store = createMockStore(mockState)
component = mountWithStore(h(
@ -61,21 +61,21 @@ describe('Dropdown components', function () {
})
it('can render two items', function () {
const items = dropdownComponent.find('li');
assert.equal(items.length, 2);
});
const items = dropdownComponent.find('li')
assert.equal(items.length, 2)
})
it('closes when item clicked', function () {
const items = dropdownComponent.find('li');
const node = items.at(0);
node.simulate('click');
assert.equal(node.props().closeMenu, closeMenu);
});
const items = dropdownComponent.find('li')
const node = items.at(0)
node.simulate('click')
assert.equal(node.props().closeMenu, closeMenu)
})
it('invokes click handler when item clicked', function () {
const items = dropdownComponent.find('li');
const node = items.at(0);
node.simulate('click');
assert.equal(onClick.calledOnce, true);
});
});
const items = dropdownComponent.find('li')
const node = items.at(0)
node.simulate('click')
assert.equal(onClick.calledOnce, true)
})
})

@ -175,6 +175,8 @@ var actions = {
CLEAR_SEND: 'CLEAR_SEND',
OPEN_FROM_DROPDOWN: 'OPEN_FROM_DROPDOWN',
CLOSE_FROM_DROPDOWN: 'CLOSE_FROM_DROPDOWN',
GAS_LOADING_STARTED: 'GAS_LOADING_STARTED',
GAS_LOADING_FINISHED: 'GAS_LOADING_FINISHED',
setGasLimit,
setGasPrice,
updateGasData,
@ -190,6 +192,8 @@ var actions = {
updateSendErrors,
clearSend,
setSelectedAddress,
gasLoadingStarted,
gasLoadingFinished,
// app messages
confirmSeedWords: confirmSeedWords,
showAccountDetail: showAccountDetail,
@ -740,8 +744,9 @@ function updateGasData ({
to,
value,
}) {
const estimatedGasPrice = estimateGasPriceFromRecentBlocks(recentBlocks)
return (dispatch) => {
dispatch(actions.gasLoadingStarted())
const estimatedGasPrice = estimateGasPriceFromRecentBlocks(recentBlocks)
return Promise.all([
Promise.resolve(estimatedGasPrice),
estimateGas({
@ -762,14 +767,28 @@ function updateGasData ({
.then((gasEstimate) => {
dispatch(actions.setGasTotal(gasEstimate))
dispatch(updateSendErrors({ gasLoadingError: null }))
dispatch(actions.gasLoadingFinished())
})
.catch(err => {
log.error(err)
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' }))
dispatch(actions.gasLoadingFinished())
})
}
}
function gasLoadingStarted () {
return {
type: actions.GAS_LOADING_STARTED,
}
}
function gasLoadingFinished () {
return {
type: actions.GAS_LOADING_FINISHED,
}
}
function updateSendTokenBalance ({
selectedToken,
tokenContract,

@ -33,6 +33,7 @@ const {
const {
getGasPrice,
getGasLimit,
getGasIsLoading,
getForceGasMin,
conversionRateSelector,
getSendAmount,
@ -51,6 +52,7 @@ function mapStateToProps (state) {
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
gasIsLoading: getGasIsLoading(state),
forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
@ -73,7 +75,7 @@ function mapDispatchToProps (dispatch) {
}
}
function getOriginalState (props) {
function getFreshState (props) {
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
@ -97,7 +99,11 @@ inherits(CustomizeGasModal, Component)
function CustomizeGasModal (props) {
Component.call(this)
this.state = getOriginalState(props)
const originalState = getFreshState(props)
this.state = {
...originalState,
originalState,
}
}
CustomizeGasModal.contextTypes = {
@ -106,6 +112,36 @@ CustomizeGasModal.contextTypes = {
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
CustomizeGasModal.prototype.componentWillReceiveProps = function (nextProps) {
const currentState = getFreshState(this.props)
const {
gasPrice: currentGasPrice,
gasLimit: currentGasLimit,
} = currentState
const newState = getFreshState(nextProps)
const {
gasPrice: newGasPrice,
gasLimit: newGasLimit,
gasTotal: newGasTotal,
} = newState
const gasPriceChanged = currentGasPrice !== newGasPrice
const gasLimitChanged = currentGasLimit !== newGasLimit
if (gasPriceChanged) {
this.setState({
gasPrice: newGasPrice,
gasTotal: newGasTotal,
priceSigZeros: '',
priceSigDec: '',
})
}
if (gasLimitChanged) {
this.setState({ gasLimit: newGasLimit, gasTotal: newGasTotal })
}
if (gasLimitChanged || gasPriceChanged) {
this.validate({ gasLimit: newGasLimit, gasTotal: newGasTotal })
}
}
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
const {
@ -137,7 +173,7 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
}
CustomizeGasModal.prototype.revert = function () {
this.setState(getOriginalState(this.props))
this.setState(this.state.originalState)
}
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
@ -233,7 +269,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
const { hideModal, forceGasMin } = this.props
const { hideModal, forceGasMin, gasIsLoading } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
@ -266,7 +302,7 @@ CustomizeGasModal.prototype.render = function () {
toNumericBase: 'dec',
})
return h('div.send-v2__customize-gas', {}, [
return !gasIsLoading && h('div.send-v2__customize-gas', {}, [
h('div.send-v2__customize-gas__content', {
}, [
h('div.send-v2__customize-gas__header', {}, [
@ -288,6 +324,7 @@ CustomizeGasModal.prototype.render = function () {
onChange: value => this.convertAndSetGasPrice(value),
title: this.context.t('gasPrice'),
copy: this.context.t('gasPriceCalculation'),
gasIsLoading,
}),
h(GasModalCard, {
@ -297,6 +334,7 @@ CustomizeGasModal.prototype.render = function () {
onChange: value => this.convertAndSetGasLimit(value),
title: this.context.t('gasLimit'),
copy: this.context.t('gasLimitCalculation'),
gasIsLoading,
}),
]),

@ -12,6 +12,7 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
const connect = require('react-redux').connect
const ToAutoComplete = require('./send/to-autocomplete')
const log = require('loglevel')
const { isValidENSAddress } = require('../util')
EnsInput.contextTypes = {
t: PropTypes.func,
@ -25,15 +26,11 @@ function EnsInput () {
Component.call(this)
}
EnsInput.prototype.render = function () {
const props = this.props
const opts = extend(props, {
list: 'addresses',
onChange: (recipient) => {
EnsInput.prototype.onChange = function (recipient) {
const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network)
props.onChange(recipient)
this.props.onChange({ toAddress: recipient })
if (!networkHasEnsSupport) return
@ -42,6 +39,7 @@ EnsInput.prototype.render = function () {
loadingEns: false,
ensResolution: null,
ensFailure: null,
toError: null,
})
}
@ -49,7 +47,13 @@ EnsInput.prototype.render = function () {
loadingEns: true,
})
this.checkName(recipient)
},
}
EnsInput.prototype.render = function () {
const props = this.props
const opts = extend(props, {
list: 'addresses',
onChange: this.onChange.bind(this),
})
return h('div', {
style: { width: '100%', position: 'relative' },
@ -85,17 +89,27 @@ EnsInput.prototype.lookupEnsName = function (recipient) {
nickname: recipient.trim(),
hoverText: address + '\n' + this.context.t('clickCopy'),
ensFailure: false,
toError: null,
})
}
})
.catch((reason) => {
log.error(reason)
return this.setState({
const setStateObj = {
loadingEns: false,
ensResolution: ZERO_ADDRESS,
ensResolution: recipient,
ensFailure: true,
hoverText: reason.message,
})
toError: null,
}
if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
setStateObj.hoverText = this.context.t('ensNameNotFound')
setStateObj.toError = 'ensNameNotFound'
setStateObj.ensFailure = false
} else {
log.error(reason)
setStateObj.hoverText = reason.message
}
return this.setState(setStateObj)
})
}
@ -105,9 +119,14 @@ EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
// If an address is sent without a nickname, meaning not from ENS or from
// the user's own accounts, a default of a one-space string is used.
const nickname = state.nickname || ' '
if (prevProps.network !== this.props.network) {
const provider = global.ethereumProvider
this.ens = new ENS({ provider, network: this.props.network })
this.onChange(ensResolution)
}
if (prevState && ensResolution && this.props.onChange &&
ensResolution !== prevState.ensResolution) {
this.props.onChange(ensResolution, nickname)
this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError })
}
}
@ -124,7 +143,9 @@ EnsInput.prototype.ensIcon = function (recipient) {
}
EnsInput.prototype.ensIconContents = function (recipient) {
const { loadingEns, ensFailure, ensResolution } = this.state || { ensResolution: ZERO_ADDRESS}
const { loadingEns, ensFailure, ensResolution, toError } = this.state || { ensResolution: ZERO_ADDRESS }
if (toError) return
if (loadingEns) {
return h('img', {

@ -22,12 +22,16 @@ function isValidInput (text) {
return re.test(text)
}
function removeLeadingZeroes (str) {
return str.replace(/^0*(?=\d)/, '')
}
InputNumber.prototype.setValue = function (newValue) {
newValue = removeLeadingZeroes(newValue)
if (newValue && !isValidInput(newValue)) return
const { fixed, min = -1, max = Infinity, onChange } = this.props
newValue = fixed ? newValue.toFixed(4) : newValue
const newValueGreaterThanMin = conversionGTE(
{ value: newValue || '0', fromNumericBase: 'dec' },
{ value: min, fromNumericBase: 'hex' },
@ -47,7 +51,7 @@ InputNumber.prototype.setValue = function (newValue) {
}
InputNumber.prototype.render = function () {
const { unitLabel, step = 1, placeholder, value = 0 } = this.props
const { unitLabel, step = 1, placeholder, value } = this.props
return h('div.customize-gas-input-wrapper', {}, [
h('input', {
@ -63,11 +67,11 @@ InputNumber.prototype.render = function () {
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
h('div.gas-tooltip-input-arrows', {}, [
h('i.fa.fa-angle-up', {
onClick: () => this.setValue(addCurrencies(value, step)),
onClick: () => this.setValue(addCurrencies(value, step, { toNumericBase: 'dec' })),
}),
h('i.fa.fa-angle-down', {
style: { cursor: 'pointer' },
onClick: () => this.setValue(subtractCurrencies(value, step)),
onClick: () => this.setValue(subtractCurrencies(value, step, { toNumericBase: 'dec' })),
}),
]),
])

@ -118,6 +118,7 @@ CurrencyDisplay.prototype.render = function () {
readOnly = false,
inError = false,
onBlur,
step,
} = this.props
const { valueToRender } = this.state
@ -149,9 +150,10 @@ CurrencyDisplay.prototype.render = function () {
} : {}),
ref: input => { this.currencyInput = input },
style: {
minWidth: this.getInputWidth(valueToRender, readOnly),
width: this.getInputWidth(valueToRender, readOnly),
},
min: 0,
step,
}),
h('span.currency-display__currency-symbol', primaryCurrency),

@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
getConvertedCurrency,
getCurrentCurrency,
} from '../send.selectors.js'
import AccountListItem from './account-list-item.component'
@ -10,6 +10,6 @@ export default connect(mapStateToProps)(AccountListItem)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
currentCurrency: getConvertedCurrency(state),
currentCurrency: getCurrentCurrency(state),
}
}

@ -12,7 +12,7 @@ proxyquire('../account-list-item.container.js', {
},
'../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
getConvertedCurrency: (s) => `mockCurrentCurrency:${s}`,
getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`,
},
})

@ -95,6 +95,7 @@ export default class SendAmountRow extends Component {
primaryCurrency={primaryCurrency || 'ETH'}
selectedToken={selectedToken}
value={amount}
step="any"
/>
</SendRowWrapper>
)

@ -2,7 +2,7 @@ import { connect } from 'react-redux'
import {
getAmountConversionRate,
getConversionRate,
getConvertedCurrency,
getCurrentCurrency,
getGasTotal,
getPrimaryCurrency,
getSelectedToken,
@ -31,7 +31,7 @@ function mapStateToProps (state) {
amountConversionRate: getAmountConversionRate(state),
balance: getSendFromBalance(state),
conversionRate: getConversionRate(state),
convertedCurrency: getConvertedCurrency(state),
convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
inError: sendAmountIsInError(state),
primaryCurrency: getPrimaryCurrency(state),

@ -24,7 +24,7 @@ proxyquire('../send-amount-row.container.js', {
'../../send.selectors': {
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
getConversionRate: (s) => `mockConversionRate:${s}`,
getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`,

@ -10,7 +10,7 @@ const propsMethodSpies = {
showCustomizeGasModal: sinon.spy(),
}
describe.only('SendGasRow Component', function() {
describe('SendGasRow Component', function () {
let wrapper
beforeEach(() => {

@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
getConvertedCurrency,
getCurrentCurrency,
getGasTotal,
} from '../../send.selectors.js'
import { sendGasIsInError } from './send-gas-row.selectors.js'
@ -13,7 +13,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
convertedCurrency: getConvertedCurrency(state),
convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
gasLoadingError: sendGasIsInError(state),
}

@ -19,7 +19,7 @@ proxyquire('../send-gas-row.container.js', {
},
'../../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
},
'./send-gas-row.selectors.js': { sendGasIsInError: (s) => `mockGasLoadingError:${s}` },

@ -19,9 +19,9 @@ export default class SendToRow extends Component {
updateSendToError: PropTypes.func,
};
handleToChange (to, nickname = '') {
handleToChange (to, nickname = '', toError) {
const { updateSendTo, updateSendToError, updateGas } = this.props
const toErrorObject = getToErrorObject(to)
const toErrorObject = getToErrorObject(to, toError)
updateSendTo(to, nickname)
updateSendToError(toErrorObject)
if (toErrorObject.to === null) {
@ -53,7 +53,7 @@ export default class SendToRow extends Component {
inError={inError}
name={'address'}
network={network}
onChange={(newTo, newNickname) => this.handleToChange(newTo, newNickname)}
onChange={({ toAddress, nickname, toError }) => this.handleToChange(toAddress, nickname, toError)}
openDropdown={() => openToDropdown()}
placeholder={this.context.t('recipientAddress')}
to={to}

@ -4,12 +4,10 @@ const {
} = require('../../send.constants')
const { isValidAddress } = require('../../../../util')
function getToErrorObject (to) {
let toError = null
function getToErrorObject (to, toError = null) {
if (!to) {
toError = REQUIRED_ERROR
} else if (!isValidAddress(to)) {
} else if (!isValidAddress(to) && !toError) {
toError = INVALID_RECIPIENT_ADDRESS_ERROR
}

@ -6,8 +6,8 @@ import proxyquire from 'proxyquire'
const SendToRow = proxyquire('../send-to-row.component.js', {
'./send-to-row.utils.js': {
getToErrorObject: (to) => ({
to: to === false ? null : `mockToErrorObject:${to}`,
getToErrorObject: (to, toError) => ({
to: to === false ? null : `mockToErrorObject:${to}${toError}`,
}),
},
}).default
@ -67,11 +67,11 @@ describe('SendToRow Component', function () {
it('should call updateSendToError', () => {
assert.equal(propsMethodSpies.updateSendToError.callCount, 0)
instance.handleToChange('mockTo2')
instance.handleToChange('mockTo2', '', 'mockToError')
assert.equal(propsMethodSpies.updateSendToError.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendToError.getCall(0).args,
[{ to: 'mockToErrorObject:mockTo2' }]
[{ to: 'mockToErrorObject:mockTo2mockToError' }]
)
})
@ -138,11 +138,11 @@ describe('SendToRow Component', function () {
openDropdown()
assert.equal(propsMethodSpies.openToDropdown.callCount, 1)
assert.equal(SendToRow.prototype.handleToChange.callCount, 0)
onChange('mockNewTo', 'mockNewNickname')
onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError' })
assert.equal(SendToRow.prototype.handleToChange.callCount, 1)
assert.deepEqual(
SendToRow.prototype.handleToChange.getCall(0).args,
['mockNewTo', 'mockNewNickname']
['mockNewTo', 'mockNewNickname', 'mockToError']
)
})
})

@ -40,6 +40,12 @@ describe('send-to-row utils', () => {
to: null,
})
})
it('should return the passed error if to is truthy but invalid if to is truthy and valid', () => {
assert.deepEqual(getToErrorObject('invalid #$ 345878', 'someExplicitError'), {
to: 'someExplicitError',
})
})
})
})

@ -36,6 +36,7 @@ const ONE_GWEI_IN_WEI_HEX = ethUtil.addHexPrefix(conversionUtil('0x1', {
}))
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
const BASE_TOKEN_GAS_COST = '0x186a0' // Hex for 100000, a base estimate for token transfers.
module.exports = {
INSUFFICIENT_FUNDS_ERROR,
@ -52,4 +53,5 @@ module.exports = {
REQUIRED_ERROR,
SIMPLE_GAS_COST,
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
BASE_TOKEN_GAS_COST,
}

@ -14,7 +14,6 @@ const selectors = {
getAmountConversionRate,
getBlockGasLimit,
getConversionRate,
getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@ -98,10 +97,6 @@ function getConversionRate (state) {
return state.metamask.conversionRate
}
function getConvertedCurrency (state) {
return state.metamask.currentCurrency
}
function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(state)

@ -10,6 +10,7 @@ const {
calcTokenAmount,
} = require('../../token-util')
const {
BASE_TOKEN_GAS_COST,
INSUFFICIENT_FUNDS_ERROR,
INSUFFICIENT_TOKENS_ERROR,
NEGATIVE_ETH_ERROR,
@ -183,6 +184,8 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
if (!code || code === '0x') {
return SIMPLE_GAS_COST
}
} else if (selectedToken && !to) {
return BASE_TOKEN_GAS_COST
}
paramsForGasEstimate.to = selectedToken ? selectedToken.address : to

@ -8,7 +8,6 @@ const {
getBlockGasLimit,
getAmountConversionRate,
getConversionRate,
getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@ -154,15 +153,6 @@ describe('send selectors', () => {
})
})
describe('getConvertedCurrency()', () => {
it('should return the currently selected currency', () => {
assert.equal(
getConvertedCurrency(mockState),
'USD'
)
})
})
describe('getCurrentAccountWithSendEtherInfo()', () => {
it('should return the currently selected account with identity info', () => {
assert.deepEqual(

@ -2,6 +2,7 @@ import assert from 'assert'
import sinon from 'sinon'
import proxyquire from 'proxyquire'
import {
BASE_TOKEN_GAS_COST,
ONE_GWEI_IN_WEI_HEX,
SIMPLE_GAS_COST,
} from '../send.constants'
@ -336,6 +337,11 @@ describe('send utils', () => {
assert.notEqual(result, SIMPLE_GAS_COST)
})
it(`should return ${BASE_TOKEN_GAS_COST} if passed a selectedToken but no to address`, async () => {
const result = await estimateGas(Object.assign({}, baseMockParams, { to: null, selectedToken: { address: '' } }))
assert.equal(result, BASE_TOKEN_GAS_COST)
})
it(`should return the adjusted blockGasLimit if it fails with a 'Transaction execution error.'`, async () => {
const result = await estimateGas(Object.assign({}, baseMockParams, {
to: 'isContract willFailBecauseOf:Transaction execution error.',

@ -181,7 +181,7 @@ ShapeshiftForm.prototype.render = function () {
return h('div.shapeshift-form-wrapper', [
showQrCode
? this.renderQrCode()
: h('div.shapeshift-form', [
: h('div.modal-shapeshift-form', [
h('div.shapeshift-form__selectors', [
h('div.shapeshift-form__selector', [

@ -204,6 +204,9 @@ SignatureRequest.prototype.renderBody = function () {
h('div.request-signature__rows', [
...rows.map(({ name, value }) => {
if (typeof value === 'boolean') {
value = value.toString()
}
return h('div.request-signature__row', [
h('div.request-signature__row-title', [`${name}:`]),
h('div.request-signature__row-value', value),

@ -48,15 +48,20 @@
display: flex;
flex: 1;
max-width: 100%;
overflow-x: scroll;
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
}
@ -69,11 +74,13 @@
.react-numeric-input {
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
display: none;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save