Merge branch 'uat-next' of https://github.com/MetaMask/metamask-extension into cb-254
commit
eb919f4710
@ -0,0 +1,34 @@ |
||||
const version = 21 |
||||
|
||||
/* |
||||
|
||||
This migration removes the BlackListController from disk state |
||||
|
||||
*/ |
||||
|
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
try { |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
} catch (err) { |
||||
console.warn(`MetaMask Migration #${version}` + err.stack) |
||||
} |
||||
return Promise.resolve(versionedData) |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
delete newState.BlacklistController |
||||
delete newState.RecentBlocks |
||||
return newState |
||||
} |
||||
|
@ -0,0 +1,26 @@ |
||||
module.exports = { |
||||
"confirm sig requests": { |
||||
signMessage: (msgData, cb) => { |
||||
const stateUpdate = { |
||||
unapprovedMsgs: {}, |
||||
unapprovedMsgCount: 0, |
||||
} |
||||
return cb(null, stateUpdate) |
||||
}, |
||||
signPersonalMessage: (msgData, cb) => { |
||||
const stateUpdate = { |
||||
unapprovedPersonalMsgs: {}, |
||||
unapprovedPersonalMsgsCount: 0, |
||||
} |
||||
return cb(null, stateUpdate) |
||||
}, |
||||
signTypedMessage: (msgData, cb) => { |
||||
const stateUpdate = { |
||||
unapprovedTypedMessages: {}, |
||||
unapprovedTypedMessagesCount: 0, |
||||
} |
||||
return cb(null, stateUpdate) |
||||
}, |
||||
}, |
||||
} |
||||
|
@ -0,0 +1,132 @@ |
||||
{ |
||||
"metamask": { |
||||
"isInitialized": true, |
||||
"isUnlocked": true, |
||||
"featureFlags": {"betaUI": true}, |
||||
"rpcTarget": "https://rawtestrpc.metamask.io/", |
||||
"identities": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"name": "Send Account 1" |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", |
||||
"name": "Send Account 2" |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", |
||||
"name": "Send Account 3" |
||||
}, |
||||
"0xd85a4b6a394794842887b8284293d69163007bbb": { |
||||
"address": "0xd85a4b6a394794842887b8284293d69163007bbb", |
||||
"name": "Send Account 4" |
||||
} |
||||
}, |
||||
"unapprovedTxs": {}, |
||||
"conversionRate": 1200.88200327, |
||||
"conversionDate": 1489013762, |
||||
"noActiveNotices": true, |
||||
"frequentRpcList": [], |
||||
"network": "3", |
||||
"accounts": { |
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { |
||||
"code": "0x", |
||||
"balance": "0x47c9d71831c76efe", |
||||
"nonce": "0x1b", |
||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" |
||||
}, |
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { |
||||
"code": "0x", |
||||
"balance": "0x37452b1315889f80", |
||||
"nonce": "0xa", |
||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" |
||||
}, |
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { |
||||
"code": "0x", |
||||
"balance": "0x30c9d71831c76efe", |
||||
"nonce": "0x1c", |
||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" |
||||
}, |
||||
"0xd85a4b6a394794842887b8284293d69163007bbb": { |
||||
"code": "0x", |
||||
"balance": "0x0", |
||||
"nonce": "0x0", |
||||
"address": "0xd85a4b6a394794842887b8284293d69163007bbb" |
||||
} |
||||
}, |
||||
"addressBook": [ |
||||
{ |
||||
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896", |
||||
"name": "Address Book Account 1" |
||||
} |
||||
], |
||||
"tokens": [], |
||||
"transactions": {}, |
||||
"selectedAddressTxList": [], |
||||
"unapprovedMsgs": {}, |
||||
"unapprovedMsgCount": 0, |
||||
"unapprovedPersonalMsgs": {}, |
||||
"unapprovedPersonalMsgCount": 0, |
||||
"keyringTypes": [ |
||||
"Simple Key Pair", |
||||
"HD Key Tree" |
||||
], |
||||
"keyrings": [ |
||||
{ |
||||
"type": "HD Key Tree", |
||||
"accounts": [ |
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825", |
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb", |
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d" |
||||
] |
||||
}, |
||||
{ |
||||
"type": "Simple Key Pair", |
||||
"accounts": [ |
||||
"0xd85a4b6a394794842887b8284293d69163007bbb" |
||||
] |
||||
} |
||||
], |
||||
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb", |
||||
"currentCurrency": "USD", |
||||
"provider": { |
||||
"type": "testnet" |
||||
}, |
||||
"shapeShiftTxList": [], |
||||
"lostAccounts": [], |
||||
"send": { |
||||
"gasLimit": null, |
||||
"gasPrice": null, |
||||
"gasTotal": "0xb451dc41b578", |
||||
"tokenBalance": null, |
||||
"from": "", |
||||
"to": "", |
||||
"amount": "0x0", |
||||
"memo": "", |
||||
"errors": {}, |
||||
"maxModeOn": false, |
||||
"editingTransactionId": null |
||||
} |
||||
}, |
||||
"appState": { |
||||
"menuOpen": false, |
||||
"currentView": { |
||||
"name": "accountDetail", |
||||
"detailView": null, |
||||
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" |
||||
}, |
||||
"accountDetail": { |
||||
"subview": "transactions" |
||||
}, |
||||
"modal": { |
||||
"modalState": {}, |
||||
"previousModalState": {} |
||||
}, |
||||
"transForward": true, |
||||
"isLoading": false, |
||||
"warning": null, |
||||
"scrollToBottom": false, |
||||
"forgottenPassword": null |
||||
}, |
||||
"identities": {} |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,153 @@ |
||||
const reactTriggerChange = require('react-trigger-change') |
||||
|
||||
QUnit.module('Add token flow') |
||||
|
||||
QUnit.test('successful add token flow', (assert) => { |
||||
const done = assert.async() |
||||
runAddTokenFlowTest(assert) |
||||
.then(done) |
||||
.catch(err => { |
||||
assert.notOk(err, `Error was thrown: ${err.stack}`) |
||||
done() |
||||
}) |
||||
}) |
||||
|
||||
async function runAddTokenFlowTest (assert, done) { |
||||
const selectState = $('select') |
||||
selectState.val('add token') |
||||
reactTriggerChange(selectState[0]) |
||||
|
||||
await timeout(2000) |
||||
|
||||
// Check that no tokens have been added
|
||||
assert.ok($('.token-list-item').length === 0, 'no tokens added') |
||||
|
||||
// Go to Add Token screen
|
||||
let addTokenButton = $('button.btn-clear.wallet-view__add-token-button') |
||||
assert.ok(addTokenButton[0], 'add token button present') |
||||
addTokenButton[0].click() |
||||
|
||||
await timeout(1000) |
||||
|
||||
// Verify Add Token screen
|
||||
let addTokenWrapper = $('.add-token__wrapper') |
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders') |
||||
|
||||
let addTokenTitle = $('.add-token__title') |
||||
assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct') |
||||
|
||||
// Cancel Add Token
|
||||
const cancelAddTokenButton = $('button.btn-cancel.add-token__button') |
||||
assert.ok(cancelAddTokenButton[0], 'cancel add token button present') |
||||
cancelAddTokenButton.click() |
||||
|
||||
await timeout(1000) |
||||
|
||||
assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view') |
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = $('button.btn-clear.wallet-view__add-token-button') |
||||
assert.ok(addTokenButton[0], 'add token button present') |
||||
addTokenButton[0].click() |
||||
|
||||
await timeout(1000) |
||||
|
||||
// Verify Add Token Screen
|
||||
addTokenWrapper = $('.add-token__wrapper') |
||||
addTokenTitle = $('.add-token__title') |
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders') |
||||
assert.equal(addTokenTitle[0].textContent, 'Add Token', 'add token title is correct') |
||||
|
||||
// Search for token
|
||||
const searchInput = $('input.add-token__input') |
||||
searchInput.val('a') |
||||
reactTriggerChange(searchInput[0]) |
||||
|
||||
await timeout() |
||||
|
||||
// Click token to add
|
||||
const tokenWrapper = $('div.add-token__token-wrapper') |
||||
assert.ok(tokenWrapper[0], 'token found') |
||||
const tokenImageProp = tokenWrapper.find('.add-token__token-icon').css('background-image') |
||||
const tokenImageUrl = tokenImageProp.slice(5, -2) |
||||
tokenWrapper[0].click() |
||||
|
||||
await timeout() |
||||
|
||||
// Click Next button
|
||||
let nextButton = $('button.btn-clear.add-token__button') |
||||
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered') |
||||
nextButton[0].click() |
||||
|
||||
await timeout() |
||||
|
||||
// Confirm Add token
|
||||
assert.equal( |
||||
$('.add-token__description')[0].textContent, |
||||
'Would you like to add these tokens?', |
||||
'confirm add token rendered' |
||||
) |
||||
assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found') |
||||
$('button.btn-clear.add-token__button')[0].click() |
||||
|
||||
await timeout(2000) |
||||
|
||||
// Verify added token image
|
||||
let heroBalance = $('.hero-balance') |
||||
assert.ok(heroBalance, 'rendered hero balance') |
||||
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added') |
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = $('button.btn-clear.wallet-view__add-token-button') |
||||
assert.ok(addTokenButton[0], 'add token button present') |
||||
addTokenButton[0].click() |
||||
|
||||
await timeout(1000) |
||||
|
||||
const addCustom = $('.add-token__add-custom') |
||||
assert.ok(addCustom[0], 'add custom token button present') |
||||
addCustom[0].click() |
||||
|
||||
await timeout() |
||||
|
||||
// Input token contract address
|
||||
const customInput = $('input.add-token__add-custom-input') |
||||
customInput.val('0x177af043D3A1Aed7cc5f2397C70248Fc6cDC056c') |
||||
reactTriggerChange(customInput[0]) |
||||
|
||||
await timeout(1000) |
||||
|
||||
// Click Next button
|
||||
nextButton = $('button.btn-clear.add-token__button') |
||||
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered') |
||||
nextButton[0].click() |
||||
|
||||
await timeout(1000) |
||||
|
||||
// Verify symbol length error since contract address won't return symbol
|
||||
const errorMessage = $('.add-token__add-custom-error-message') |
||||
assert.ok(errorMessage[0], 'error rendered') |
||||
$('button.btn-cancel.add-token__button')[0].click() |
||||
|
||||
await timeout(2000) |
||||
|
||||
// // Confirm Add token
|
||||
// assert.equal(
|
||||
// $('.add-token__description')[0].textContent,
|
||||
// 'Would you like to add these tokens?',
|
||||
// 'confirm add token rendered'
|
||||
// )
|
||||
// assert.ok($('button.btn-clear.add-token__button')[0], 'confirm add token button found')
|
||||
// $('button.btn-clear.add-token__button')[0].click()
|
||||
|
||||
// // Verify added token image
|
||||
// heroBalance = $('.hero-balance')
|
||||
// assert.ok(heroBalance, 'rendered hero balance')
|
||||
// assert.ok(heroBalance.find('.identicon')[0], 'token added')
|
||||
} |
||||
|
||||
function timeout (time) { |
||||
return new Promise((resolve, reject) => { |
||||
setTimeout(resolve, time || 1500) |
||||
}) |
||||
} |
@ -0,0 +1,67 @@ |
||||
const reactTriggerChange = require('react-trigger-change') |
||||
|
||||
const PASSWORD = 'password123' |
||||
|
||||
QUnit.module('confirm sig requests') |
||||
|
||||
QUnit.test('successful confirmation of sig requests', (assert) => { |
||||
const done = assert.async() |
||||
runConfirmSigRequestsTest(assert).then(done).catch((err) => { |
||||
assert.notOk(err, `Error was thrown: ${err.stack}`) |
||||
done() |
||||
}) |
||||
}) |
||||
|
||||
async function runConfirmSigRequestsTest(assert, done) { |
||||
let selectState = $('select') |
||||
selectState.val('confirm sig requests') |
||||
reactTriggerChange(selectState[0]) |
||||
|
||||
await timeout(2000) |
||||
|
||||
let confirmSigHeadline = $('.request-signature__headline') |
||||
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') |
||||
|
||||
let confirmSigRowValue = $('.request-signature__row-value') |
||||
assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/)) |
||||
|
||||
let confirmSigSignButton = $('.request-signature__footer__sign-button') |
||||
confirmSigSignButton[0].click() |
||||
|
||||
await timeout(2000) |
||||
|
||||
confirmSigHeadline = $('.request-signature__headline') |
||||
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') |
||||
|
||||
let confirmSigMessage = $('.request-signature__notice') |
||||
assert.ok(confirmSigMessage[0].textContent.match(/^Signing\sthis\smessage/)) |
||||
|
||||
confirmSigRowValue = $('.request-signature__row-value') |
||||
assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0') |
||||
|
||||
confirmSigSignButton = $('.request-signature__footer__sign-button') |
||||
confirmSigSignButton[0].click() |
||||
|
||||
await timeout(2000) |
||||
|
||||
confirmSigHeadline = $('.request-signature__headline') |
||||
assert.equal(confirmSigHeadline[0].textContent, 'Your signature is being requested') |
||||
|
||||
confirmSigRowValue = $('.request-signature__row-value') |
||||
assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!') |
||||
assert.equal(confirmSigRowValue[1].textContent, '1337') |
||||
|
||||
confirmSigSignButton = $('.request-signature__footer__sign-button') |
||||
confirmSigSignButton[0].click() |
||||
|
||||
await timeout(2000) |
||||
|
||||
const txView = $('.tx-view') |
||||
assert.ok(txView[0], 'Should return to the account details screen after confirming') |
||||
} |
||||
|
||||
function timeout (time) { |
||||
return new Promise((resolve, reject) => { |
||||
setTimeout(resolve, time || 1500) |
||||
}) |
||||
} |
File diff suppressed because one or more lines are too long
@ -1,16 +1,20 @@ |
||||
const { shallow, mount } = require('enzyme') |
||||
|
||||
exports.shallowWithStore = function shallowWithStore (component, store) { |
||||
module.exports = { |
||||
shallowWithStore, |
||||
mountWithStore, |
||||
} |
||||
|
||||
function shallowWithStore (component, store) { |
||||
const context = { |
||||
store, |
||||
} |
||||
|
||||
return shallow(component, { context }) |
||||
return shallow(component, {context}) |
||||
} |
||||
|
||||
exports.mountWithStore = function mountWithStore (component, store) { |
||||
function mountWithStore (component, store) { |
||||
const context = { |
||||
store, |
||||
} |
||||
return mount(component, { context }) |
||||
return mount(component, {context}) |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
const assert = require('assert') |
||||
|
||||
const wallet2 = require('../../lib/migrations/002.json') |
||||
const migration21 = require('../../../app/scripts/migrations/021') |
||||
|
||||
describe('wallet2 is migrated successfully with out the BlacklistController', () => { |
||||
it('should delete BlacklistController key', (done) => { |
||||
migration21.migrate(wallet2) |
||||
.then((migratedData) => { |
||||
assert.equal(migratedData.meta.version, 21) |
||||
assert(!migratedData.data.BlacklistController) |
||||
assert(!migratedData.data.RecentBlocks) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
}) |
@ -0,0 +1,43 @@ |
||||
const assert = require('assert') |
||||
const { createMockStore } = require('redux-test-utils') |
||||
const h = require('react-hyperscript') |
||||
const { shallowWithStore } = require('../../lib/shallow-with-store') |
||||
const AddTokenScreen = require('../../../old-ui/app/add-token') |
||||
|
||||
describe('Add Token Screen', function () { |
||||
let addTokenComponent, store, component |
||||
const mockState = { |
||||
metamask: { |
||||
identities: { |
||||
'0x7d3517b0d011698406d6e0aed8453f0be2697926': { |
||||
'address': '0x7d3517b0d011698406d6e0aed8453f0be2697926', |
||||
'name': 'Add Token Name', |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
beforeEach(function () { |
||||
store = createMockStore(mockState) |
||||
component = shallowWithStore(h(AddTokenScreen), store) |
||||
addTokenComponent = component.dive() |
||||
}) |
||||
|
||||
describe('#ValidateInputs', function () { |
||||
|
||||
it('Default State', function () { |
||||
addTokenComponent.instance().validateInputs() |
||||
const state = addTokenComponent.state() |
||||
assert.equal(state.warning, 'Address is invalid.') |
||||
}) |
||||
|
||||
it('Address is a Metamask Identity', function () { |
||||
addTokenComponent.setState({ |
||||
address: '0x7d3517b0d011698406d6e0aed8453f0be2697926', |
||||
}) |
||||
addTokenComponent.instance().validateInputs() |
||||
const state = addTokenComponent.state() |
||||
assert.equal(state.warning, 'Personal address detected. Input the token contract address.') |
||||
}) |
||||
|
||||
}) |
||||
}) |
@ -0,0 +1,125 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const connect = require('react-redux').connect |
||||
const actions = require('./actions') |
||||
const getCaretCoordinates = require('textarea-caret') |
||||
const EventEmitter = require('events').EventEmitter |
||||
|
||||
const Mascot = require('./components/mascot') |
||||
|
||||
module.exports = connect(mapStateToProps)(UnlockScreen) |
||||
|
||||
inherits(UnlockScreen, Component) |
||||
function UnlockScreen () { |
||||
Component.call(this) |
||||
this.animationEventEmitter = new EventEmitter() |
||||
} |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
warning: state.appState.warning, |
||||
} |
||||
} |
||||
|
||||
UnlockScreen.prototype.render = function () { |
||||
const state = this.props |
||||
const warning = state.warning |
||||
return ( |
||||
h('.flex-column', { |
||||
style: { |
||||
width: 'inherit', |
||||
}, |
||||
}, [ |
||||
h('.unlock-screen.flex-column.flex-center.flex-grow', [ |
||||
|
||||
h(Mascot, { |
||||
animationEventEmitter: this.animationEventEmitter, |
||||
}), |
||||
|
||||
h('h1', { |
||||
style: { |
||||
fontSize: '1.4em', |
||||
textTransform: 'uppercase', |
||||
color: '#7F8082', |
||||
}, |
||||
}, 'MetaMask'), |
||||
|
||||
h('input.large-input', { |
||||
type: 'password', |
||||
id: 'password-box', |
||||
placeholder: 'enter password', |
||||
style: { |
||||
background: 'white', |
||||
}, |
||||
onKeyPress: this.onKeyPress.bind(this), |
||||
onInput: this.inputChanged.bind(this), |
||||
}), |
||||
|
||||
h('.error', { |
||||
style: { |
||||
display: warning ? 'block' : 'none', |
||||
padding: '0 20px', |
||||
textAlign: 'center', |
||||
}, |
||||
}, warning), |
||||
|
||||
h('button.primary.cursor-pointer', { |
||||
onClick: this.onSubmit.bind(this), |
||||
style: { |
||||
margin: 10, |
||||
}, |
||||
}, 'Unlock'), |
||||
]), |
||||
|
||||
h('.flex-row.flex-center.flex-grow', [ |
||||
h('p.pointer', { |
||||
onClick: () => { |
||||
this.props.dispatch(actions.markPasswordForgotten()) |
||||
global.platform.openExtensionInBrowser() |
||||
}, |
||||
style: { |
||||
fontSize: '0.8em', |
||||
color: 'rgb(247, 134, 28)', |
||||
textDecoration: 'underline', |
||||
}, |
||||
}, 'Restore from seed phrase'), |
||||
]), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
UnlockScreen.prototype.componentDidMount = function () { |
||||
document.getElementById('password-box').focus() |
||||
} |
||||
|
||||
UnlockScreen.prototype.onSubmit = function (event) { |
||||
const input = document.getElementById('password-box') |
||||
const password = input.value |
||||
this.props.dispatch(actions.tryUnlockMetamask(password)) |
||||
} |
||||
|
||||
UnlockScreen.prototype.onKeyPress = function (event) { |
||||
if (event.key === 'Enter') { |
||||
this.submitPassword(event) |
||||
} |
||||
} |
||||
|
||||
UnlockScreen.prototype.submitPassword = function (event) { |
||||
var element = event.target |
||||
var password = element.value |
||||
// reset input
|
||||
element.value = '' |
||||
this.props.dispatch(actions.tryUnlockMetamask(password)) |
||||
} |
||||
|
||||
UnlockScreen.prototype.inputChanged = function (event) { |
||||
// tell mascot to look at page action
|
||||
var element = event.target |
||||
var boundingRect = element.getBoundingClientRect() |
||||
var coordinates = getCaretCoordinates(element, element.selectionEnd) |
||||
this.animationEventEmitter.emit('point', { |
||||
x: boundingRect.left + coordinates.left - element.scrollLeft, |
||||
y: boundingRect.top + coordinates.top - element.scrollTop, |
||||
}) |
||||
} |
Loading…
Reference in new issue