1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Various component tests and some conditional statements (#7765)

* Various component tests and some conditional statements

Conditional in account-menu in removeAccount when keyring sometimes is not initially provideed
Conditional on unlock-page when there is no target.getBoundingClientRect on the element.

* Update helpers

* Remove component debugging

* Add default params for render helpers

* Remove stubComponent for old Mascot
Changes in https://github.com/MetaMask/metamask-extension/pull/7893 has prevented the need to stub it out.

Change logout to lock in account-menu test
This commit is contained in:
Thomas Huang 2020-01-30 11:34:45 -08:00 committed by GitHub
parent 608a1fc298
commit 5d1c9313db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 2472 additions and 26 deletions

View File

@ -56,6 +56,9 @@ require('jsdom-global')()
// localStorage
window.localStorage = {}
// override metamask-logo
window.requestAnimationFrame = () => {}
// crypto.getRandomValues
if (!window.crypto) {
window.crypto = {}

View File

@ -1,14 +1,7 @@
import { shallow, mount } from 'enzyme'
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import { shape } from 'prop-types'
export function shallowWithStore (component, store) {
const context = {
store,
}
return shallow(component, { context })
}
import { mount } from 'enzyme'
import { MemoryRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
export function mountWithStore (component, store) {
const context = {
@ -17,26 +10,40 @@ export function mountWithStore (component, store) {
return mount(component, { context })
}
export function mountWithRouter (node) {
export function mountWithRouter (component, store = {}, pathname = '/') {
// Instantiate router context
const router = {
history: new BrowserRouter().history,
history: new MemoryRouter().history,
route: {
location: {},
location: {
pathname: pathname,
},
match: {},
},
}
const createContext = () => ({
context: { router, t: () => {} },
childContextTypes: { router: shape({}), t: () => {} },
context: {
router,
t: str => str,
tOrKey: str => str,
metricsEvent: () => {},
store,
},
childContextTypes: {
router: PropTypes.object,
t: PropTypes.func,
tOrKey: PropTypes.func,
metricsEvent: PropTypes.func,
store: PropTypes.object,
},
})
const Wrapper = () => (
<BrowserRouter>
{node}
</BrowserRouter>
<MemoryRouter initialEntries={[{ pathname }]} initialIndex={0}>
{component}
</MemoryRouter>
)
return mount(<Wrapper />, createContext())

View File

@ -193,6 +193,12 @@ export default class AccountMenu extends Component {
renderRemoveAccount (keyring, identity) {
const { t } = this.context
// Sometimes keyrings aren't loaded yet
if (!keyring) {
return null
}
// Any account that's not from the HD wallet Keyring can be removed
const { type } = keyring
const isRemovable = type !== 'HD Key Tree'

View File

@ -0,0 +1,209 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import configureMockStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import AccountMenu from '../index'
import { Provider } from 'react-redux'
describe('Account Menu', async () => {
let wrapper
const mockStore = {
metamask: {
provider: {
type: 'test',
},
preferences: {
useNativeCurrencyAsPrimaryCurrency: true,
},
},
}
const store = configureMockStore()(mockStore)
const props = {
isAccountMenuOpen: true,
addressConnectedDomainMap: {},
accounts: [
{
address: '0xAddress',
name: 'Account 1',
balance: '0x0',
},
{
address: '0xImportedAddress',
name: 'Imported Account 1',
balance: '0x0',
},
],
keyrings: [
{
type: 'HD Key Tree',
accounts: [
'0xAdress',
],
},
{
type: 'Simple Key Pair',
accounts: [
'0xImportedAddress',
],
},
],
prevIsAccountMenuOpen: false,
lockMetamask: sinon.spy(),
showAccountDetail: sinon.spy(),
showRemoveAccountConfirmationModal: sinon.spy(),
toggleAccountMenu: sinon.spy(),
history: {
push: sinon.spy(),
},
}
before(() => {
wrapper = mountWithRouter(
<Provider store={store}>
<AccountMenu.WrappedComponent {...props} />
</Provider>, store
)
})
afterEach(() => {
props.toggleAccountMenu.resetHistory()
props.history.push.resetHistory()
})
describe('Render Content', () => {
it('returns account name from identities', () => {
const accountName = wrapper.find('.account-menu__name')
assert.equal(accountName.length, 2)
})
it('renders user preference currency display balance from account balance', () => {
const accountBalance = wrapper.find('.currency-display-component.account-menu__balance')
assert.equal(accountBalance.length, 2)
})
it('simulate click', () => {
const click = wrapper.find('.account-menu__account.menu__item--clickable')
click.first().simulate('click')
assert(props.showAccountDetail.calledOnce)
assert.equal(props.showAccountDetail.getCall(0).args[0], '0xAddress')
})
it('render imported account label', () => {
const importedAccount = wrapper.find('.keyring-label.allcaps')
assert.equal(importedAccount.text(), 'imported')
})
it('remove account', () => {
const removeAccount = wrapper.find('.remove-account-icon')
removeAccount.simulate('click', {
preventDefault: () => {},
stopPropagation: () => {},
})
assert(props.showRemoveAccountConfirmationModal.calledOnce)
assert.deepEqual(props.showRemoveAccountConfirmationModal.getCall(0).args[0],
{ address: '0xImportedAddress', balance: '0x0', name: 'Imported Account 1' }
)
})
})
describe('Log Out', () => {
let logout
it('logout', () => {
logout = wrapper.find('.account-menu__lock-button')
assert.equal(logout.length, 1)
})
it('simulate click', () => {
logout.simulate('click')
assert(props.lockMetamask.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/')
})
})
describe('Create Account', () => {
let createAccount
it('renders create account item', () => {
createAccount = wrapper.find({ text: 'createAccount' })
assert.equal(createAccount.length, 1)
})
it('calls toggle menu and push new-account route to history', () => {
createAccount.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/new-account')
})
})
describe('Import Account', () => {
let importAccount
it('renders import account item', () => {
importAccount = wrapper.find({ text: 'importAccount' })
assert.equal(importAccount.length, 1)
})
it('calls toggle menu and push /new-account/import route to history', () => {
importAccount.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
assert(props.history.push.getCall(0).args[0], '/new-account/import')
})
})
describe('Connect Hardware Wallet', () => {
let connectHardwareWallet
it('renders import account item', () => {
connectHardwareWallet = wrapper.find({ text: 'connectHardwareWallet' })
assert.equal(connectHardwareWallet.length, 1)
})
it('calls toggle menu and push /new-account/connect route to history', () => {
connectHardwareWallet.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/new-account/connect')
})
})
describe('Info & Help', () => {
let infoHelp
it('renders import account item', () => {
infoHelp = wrapper.find({ text: 'infoHelp' })
assert.equal(infoHelp.length, 1)
})
it('calls toggle menu and push /new-account/connect route to history', () => {
infoHelp.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/settings/about-us')
})
})
describe('Settings', () => {
let settings
it('renders import account item', () => {
settings = wrapper.find({ text: 'settings' })
assert.equal(settings.length, 1)
})
it('calls toggle menu and push /new-account/connect route to history', () => {
settings.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/settings')
})
})
})

View File

@ -0,0 +1,99 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { shallow } from 'enzyme'
import MetaFoxLogo from '../../../ui/metafox-logo'
import AppHeader from '../index'
describe('App Header', () => {
let wrapper
const props = {
hideNetworkDropdown: sinon.spy(),
showNetworkDropdown: sinon.spy(),
toggleAccountMenu: sinon.spy(),
history: {
push: sinon.spy(),
},
network: 'test',
provider: {},
selectedAddress: '0xAddress',
disabled: false,
hideNetworkIndicator: false,
networkDropdownOpen: false,
isAccountMenuOpen: false,
isUnlocked: true,
}
beforeEach(() => {
wrapper = shallow(
<AppHeader.WrappedComponent {...props} />, {
context: {
t: str => str,
metricsEvent: () => {},
},
}
)
})
afterEach(() => {
props.toggleAccountMenu.resetHistory()
})
describe('App Header Logo', () => {
it('routes to default route when logo is clicked', () => {
const appLogo = wrapper.find(MetaFoxLogo)
appLogo.simulate('click')
assert(props.history.push.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/')
})
})
describe('Network', () => {
it('shows network dropdown when networkDropdownOpen is false', () => {
const network = wrapper.find({ network: 'test' })
network.simulate('click', {
preventDefault: () => {},
stopPropagation: () => {},
})
assert(props.showNetworkDropdown.calledOnce)
})
it('hides network dropdown when networkDropdownOpen is true', () => {
wrapper.setProps({ networkDropdownOpen: true })
const network = wrapper.find({ network: 'test' })
network.simulate('click', {
preventDefault: () => {},
stopPropagation: () => {},
})
assert(props.hideNetworkDropdown.calledOnce)
})
it('hides network indicator', () => {
wrapper.setProps({ hideNetworkIndicator: true })
const network = wrapper.find({ network: 'test' })
assert.equal(network.length, 0)
})
})
describe('Account Menu', () => {
it('toggles account menu', () => {
const accountMenu = wrapper.find('.account-menu__icon')
accountMenu.simulate('click')
assert(props.toggleAccountMenu.calledOnce)
})
it('does not toggle account menu when disabled', () => {
wrapper.setProps({ disabled: true })
const accountMenu = wrapper.find('.account-menu__icon')
accountMenu.simulate('click')
assert(props.toggleAccountMenu.notCalled)
})
})
})

View File

@ -40,7 +40,7 @@ class AccountDetailsDropdown extends Component {
static propTypes = {
selectedIdentity: PropTypes.object.isRequired,
network: PropTypes.number.isRequired,
network: PropTypes.string.isRequired,
keyrings: PropTypes.array.isRequired,
showAccountDetailModal: PropTypes.func.isRequired,
viewOnEtherscan: PropTypes.func.isRequired,

View File

@ -0,0 +1,104 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import AdvancedTabContent from '../index'
describe('Advanced Gas Inputs', () => {
let wrapper, clock
const props = {
updateCustomGasPrice: sinon.spy(),
updateCustomGasLimit: sinon.spy(),
showGasPriceInfoModal: sinon.spy(),
showGasLimitInfoModal: sinon.spy(),
customGasPrice: 0,
customGasLimit: 0,
insufficientBalance: false,
customPriceIsSafe: true,
isSpeedUp: false,
}
beforeEach(() => {
clock = sinon.useFakeTimers()
wrapper = mount(
<AdvancedTabContent.WrappedComponent
{...props}
/>, {
context: {
t: str => str,
},
})
})
afterEach(() => {
clock.restore()
})
it('wont update gasPrice in props before debounce', () => {
const event = { target: { value: 1 } }
wrapper.find('input').at(0).simulate('change', event)
clock.tick(499)
assert.equal(props.updateCustomGasPrice.callCount, 0)
})
it('simulates onChange on gas price after debounce', () => {
const event = { target: { value: 1 } }
wrapper.find('input').at(0).simulate('change', event)
clock.tick(500)
assert.equal(props.updateCustomGasPrice.calledOnce, true)
assert.equal(props.updateCustomGasPrice.calledWith(1), true)
})
it('wont update gasLimit in props before debounce', () => {
const event = { target: { value: 21000 } }
wrapper.find('input').at(1).simulate('change', event)
clock.tick(499)
assert.equal(props.updateCustomGasLimit.callCount, 0)
})
it('simulates onChange on gas limit after debounce', () => {
const event = { target: { value: 21000 } }
wrapper.find('input').at(1).simulate('change', event)
clock.tick(500)
assert.equal(props.updateCustomGasLimit.calledOnce, true)
assert.equal(props.updateCustomGasLimit.calledWith(21000), true)
})
it('errors when insuffientBalance under gas price and gas limit', () => {
wrapper.setProps({ insufficientBalance: true })
const renderError = wrapper.find('.advanced-gas-inputs__gas-edit-row__error-text')
assert.equal(renderError.length, 2)
assert.equal(renderError.at(0).text(), 'insufficientBalance')
assert.equal(renderError.at(1).text(), 'insufficientBalance')
})
it('errors zero gas price / speed up', () => {
wrapper.setProps({ isSpeedUp: true })
const renderError = wrapper.find('.advanced-gas-inputs__gas-edit-row__error-text')
assert.equal(renderError.length, 2)
assert.equal(renderError.at(0).text(), 'zeroGasPriceOnSpeedUpError')
assert.equal(renderError.at(1).text(), 'gasLimitTooLow')
})
it('warns when custom gas price is too low', () => {
wrapper.setProps({ customPriceIsSafe: false })
const renderWarning = wrapper.find('.advanced-gas-inputs__gas-edit-row__warning-text')
assert.equal(renderWarning.length, 1)
assert.equal(renderWarning.text(), 'gasPriceExtremelyLow')
})
})

View File

@ -0,0 +1,37 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { shallow } from 'enzyme'
import InfoBox from '../index'
describe('InfoBox', () => {
let wrapper
const props = {
title: 'Title',
description: 'Description',
onClose: sinon.spy(),
}
beforeEach(() => {
wrapper = shallow(<InfoBox {...props} />)
})
it('renders title from props', () => {
const title = wrapper.find('.info-box__title')
assert.equal(title.text(), props.title)
})
it('renders description from props', () => {
const description = wrapper.find('.info-box__description')
assert.equal(description.text(), props.description)
})
it('closes info box', () => {
const close = wrapper.find('.info-box__close')
close.simulate('click')
assert(props.onClose.calledOnce)
})
})

View File

@ -0,0 +1,94 @@
import React from 'react'
import configureStore from 'redux-mock-store'
import assert from 'assert'
import sinon from 'sinon'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import MenuBar from '../index'
import { Provider } from 'react-redux'
describe('MenuBar', () => {
let wrapper
const mockStore = {
metamask: {
network: '1',
selectedAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
identities: {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
name: 'Account 1',
},
},
keyrings: [
{
type: 'HD Key Tree',
accounts: [
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
],
},
],
frequentRpcListDetail: [],
},
appState: {
sidebar: {
isOpen: false,
},
},
}
const store = configureStore()(mockStore)
afterEach(() => {
sinon.restore()
})
it('shows side bar when sidbarOpen is set to false', () => {
const props = {
showSidebar: sinon.spy(),
}
wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar.WrappedComponent {...props} />
</Provider>, store
)
const sidebarButton = wrapper.find('.menu-bar__sidebar-button')
sidebarButton.simulate('click')
assert(props.showSidebar.calledOnce)
})
it('hides side when sidebarOpen is set to true', () => {
const props = {
showSidebar: sinon.spy(),
hideSidebar: sinon.spy(),
sidebarOpen: true,
}
wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar.WrappedComponent {...props} />
</Provider>, store
)
const sidebarButton = wrapper.find('.menu-bar__sidebar-button')
sidebarButton.prop('onClick')()
assert(props.hideSidebar.calledOnce)
})
it('opens account detail menu when account options is clicked', () => {
const accountOptions = wrapper.find('.menu-bar__open-in-browser')
accountOptions.simulate('click')
assert.equal(wrapper.find('MenuBar').instance().state.accountDetailsMenuOpen, true)
})
it('sets accountDetailsMenuOpen to false when closed', () => {
wrapper.find('MenuBar').instance().setState({ accountDetailsMenuOpen: true })
wrapper.update()
const accountDetailsMenu = wrapper.find('AccountDetailsDropdown')
accountDetailsMenu.prop('onClose')()
assert.equal(wrapper.find('MenuBar').instance().state.accountDetailsMenuOpen, false)
})
})

View File

@ -0,0 +1,58 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import ConfirmDeleteNetwork from '../index'
describe('Confirm Delete Network', () => {
let wrapper
const props = {
hideModal: sinon.spy(),
delRpcTarget: sinon.stub().resolves(),
onConfirm: sinon.spy(),
target: '',
}
beforeEach(() => {
wrapper = mount(
<ConfirmDeleteNetwork.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
afterEach(() => {
props.hideModal.resetHistory()
props.delRpcTarget.resetHistory()
props.onConfirm.resetHistory()
})
it('renders delete network modal title', () => {
const modalTitle = wrapper.find('.modal-content__title')
assert.equal(modalTitle.text(), 'deleteNetwork')
})
it('clicks cancel to hide modal', () => {
const cancelButton = wrapper.find('.button.btn-default.modal-container__footer-button')
cancelButton.simulate('click')
assert(props.hideModal.calledOnce)
})
it('clicks delete to delete the target and hides modal', () => {
const deleteButton = wrapper.find('.button.btn-danger.modal-container__footer-button')
deleteButton.simulate('click')
setImmediate(() => {
assert(props.delRpcTarget.calledOnce)
assert(props.hideModal.calledOnce)
assert(props.onConfirm.calledOnce)
})
})
})

View File

@ -0,0 +1,82 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import assert from 'assert'
import sinon from 'sinon'
import configureStore from 'redux-mock-store'
import { mount } from 'enzyme'
import ConfirmRemoveAccount from '../index'
describe('Confirm Remove Account', () => {
let wrapper
const state = {
metamask: {
},
}
const props = {
hideModal: sinon.spy(),
removeAccount: sinon.stub().resolves(),
network: '101',
identity: {
address: '0xAddress',
name: 'Account 1',
},
}
const mockStore = configureStore()
const store = mockStore(state)
beforeEach(() => {
wrapper = mount(
<Provider store={store} >
<ConfirmRemoveAccount.WrappedComponent {...props} />
</Provider>, {
context: {
t: str => str,
store,
},
childContextTypes: {
t: PropTypes.func,
store: PropTypes.object,
},
}
)
})
afterEach(() => {
props.hideModal.resetHistory()
})
it('nevermind', () => {
const nevermind = wrapper.find({ type: 'default' })
nevermind.simulate('click')
assert(props.hideModal.calledOnce)
})
it('remove', (done) => {
const remove = wrapper.find({ type: 'secondary' })
remove.simulate('click')
assert(props.removeAccount.calledOnce)
assert.equal(props.removeAccount.getCall(0).args[0], props.identity.address)
setImmediate(() => {
assert(props.hideModal.calledOnce)
done()
})
})
it('closes', () => {
const close = wrapper.find('.modal-container__header-close')
close.simulate('click')
assert(props.hideModal.calledOnce)
})
})

View File

@ -0,0 +1,46 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import ConfirmResetAccount from '../index'
describe('Confirm Reset Account', () => {
let wrapper
const props = {
hideModal: sinon.spy(),
resetAccount: sinon.stub().resolves(),
}
beforeEach(() => {
wrapper = mount(
<ConfirmResetAccount.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
afterEach(() => {
props.hideModal.resetHistory()
})
it('hides modal when nevermind button is clicked', () => {
const nevermind = wrapper.find('.btn-default.modal-container__footer-button')
nevermind.simulate('click')
assert(props.hideModal.calledOnce)
})
it('resets account and hidels modal when reset button is clicked', (done) => {
const reset = wrapper.find('.btn-danger.modal-container__footer-button')
reset.simulate('click')
setImmediate(() => {
assert(props.resetAccount.calledOnce)
assert(props.hideModal.calledOnce)
done()
})
})
})

View File

@ -0,0 +1,55 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import MetaMetricsOptIn from '../index'
describe('MetaMetrics Opt In', () => {
let wrapper
const props = {
setParticipateInMetaMetrics: sinon.stub().resolves(),
hideModal: sinon.spy(),
participateInMetaMetrics: null,
}
beforeEach(() => {
wrapper = mount(
<MetaMetricsOptIn.WrappedComponent {...props} />, {
context: {
metricsEvent: () => {},
},
}
)
})
afterEach(() => {
props.setParticipateInMetaMetrics.resetHistory()
props.hideModal.resetHistory()
})
it('passes false to setParticipateInMetaMetrics and hides modal', (done) => {
const noThanks = wrapper.find('.btn-default.page-container__footer-button')
noThanks.simulate('click')
setImmediate(() => {
assert(props.setParticipateInMetaMetrics.calledOnce)
assert.equal(props.setParticipateInMetaMetrics.getCall(0).args[0], false)
assert(props.hideModal.calledOnce)
done()
})
})
it('passes true to setParticipateInMetaMetrics and hides modal', (done) => {
const iAgree = wrapper.find('.btn-primary.page-container__footer-button')
iAgree.simulate('click')
setImmediate(() => {
assert(props.setParticipateInMetaMetrics.calledOnce)
assert.equal(props.setParticipateInMetaMetrics.getCall(0).args[0], true)
assert(props.hideModal.calledOnce)
done()
})
})
})

View File

@ -0,0 +1,48 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import RejectTransactionsModal from '../index'
describe('Reject Transactions Model', () => {
let wrapper
const props = {
onSubmit: sinon.spy(),
hideModal: sinon.spy(),
unapprovedTxCount: 2,
}
beforeEach(() => {
wrapper = mount(
<RejectTransactionsModal.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
afterEach(() => {
props.hideModal.resetHistory()
})
it('hides modal when cancel button is clicked', () => {
const cancelButton = wrapper.find('.btn-default.modal-container__footer-button')
cancelButton.simulate('click')
assert(props.hideModal.calledOnce)
})
it('onSubmit is called and hides modal when reject all clicked', (done) => {
const rejectAllButton = wrapper.find('.btn-secondary.modal-container__footer-button')
rejectAllButton.simulate('click')
setImmediate(() => {
assert(props.onSubmit.calledOnce)
assert(props.hideModal.calledOnce)
done()
})
})
})

View File

@ -0,0 +1,82 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { shallow } from 'enzyme'
import AccountDetailsModal from '../account-details-modal'
describe('Account Details Modal', () => {
let wrapper
global.platform = { openWindow: sinon.spy() }
const props = {
hideModal: sinon.spy(),
setAccountLabel: sinon.spy(),
showExportPrivateKeyModal: sinon.spy(),
showQrView: sinon.spy(),
network: 'test',
rpcPrefs: {},
selectedIdentity: {
address: '0xAddress',
name: 'Account 1',
},
keyrings: [
{
type: 'HD Key Tree',
accounts: [
'0xAddress',
],
},
],
identities: {
'0xAddress': {
address: '0xAddress',
name: 'Account 1',
},
},
}
beforeEach(() => {
wrapper = shallow(
<AccountDetailsModal.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
it('sets account label when changing default account label', () => {
const accountLabel = wrapper.find('.account-modal__name').first()
accountLabel.simulate('submit', 'New Label')
assert(props.setAccountLabel.calledOnce)
assert.equal(props.setAccountLabel.getCall(0).args[1], 'New Label')
})
it('opens new window when view block explorer is clicked', () => {
const modalButton = wrapper.find('.account-modal__button')
const etherscanLink = modalButton.first()
etherscanLink.simulate('click')
assert(global.platform.openWindow.calledOnce)
})
it('shows export private key modal when clicked', () => {
const modalButton = wrapper.find('.account-modal__button')
const etherscanLink = modalButton.last()
etherscanLink.simulate('click')
assert(props.showExportPrivateKeyModal.calledOnce)
})
it('sets blockexplorerview text when block explorer url in rpcPrefs exists', () => {
const blockExplorerUrl = 'https://block.explorer'
wrapper.setProps({ rpcPrefs: { blockExplorerUrl } })
const modalButton = wrapper.find('.account-modal__button')
const blockExplorerLink = modalButton.first()
assert.equal(blockExplorerLink.html(), '<button class="button btn-secondary account-modal__button">blockExplorerView</button>')
})
})

View File

@ -0,0 +1,32 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import TransactionConfirmed from '../index'
describe('Transaction Confirmed', () => {
let wrapper
const props = {
onSubmit: sinon.spy(),
hideModal: sinon.spy(),
}
beforeEach(() => {
wrapper = mount(
<TransactionConfirmed.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
it('clicks ok to submit and hide modal', () => {
const submit = wrapper.find('.btn-secondary.modal-container__footer-button')
submit.simulate('click')
assert(props.onSubmit.calledOnce)
assert(props.hideModal.calledOnce)
})
})

View File

@ -0,0 +1,69 @@
import React from 'react'
import { Provider } from 'react-redux'
import assert from 'assert'
import sinon from 'sinon'
import configureMockStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../test/lib/render-helpers'
import SignatureRequest from '../signature-request'
describe('Signature Request', () => {
let wrapper
const mockStore = {
metamask: {
provider: {
type: 'test',
},
},
}
const store = configureMockStore()(mockStore)
const props = {
selectedAccount: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
history: {
push: sinon.spy(),
},
clearConfirmTransaction: sinon.spy(),
cancelMessage: sinon.spy(),
cancel: sinon.stub().resolves(),
sign: sinon.stub().resolves(),
txData: {
msgParams: {
id: 1,
data: '{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"4","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","wallet":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},"contents":"Hello, Bob!"}}',
from: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
origin: 'test.domain',
},
status: 'unapproved',
time: 1,
type: 'eth_sign',
},
}
beforeEach(() => {
wrapper = mountWithRouter(
<Provider store={store}>
<SignatureRequest.WrappedComponent {...props} />
</Provider>, store
)
})
afterEach(() => {
props.clearConfirmTransaction.resetHistory()
})
it('cancel', () => {
const cancelButton = wrapper.find('button.btn-default')
cancelButton.simulate('click')
assert(props.cancel.calledOnce)
})
it('sign', () => {
const signButton = wrapper.find('button.btn-primary')
signButton.simulate('click')
assert(props.sign.calledOnce)
})
})

View File

@ -0,0 +1,69 @@
import React from 'react'
import assert from 'assert'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import configureMockStore from 'redux-mock-store'
import { mount } from 'enzyme'
import TokenCell from '../token-cell'
import Identicon from '../../ui/identicon'
describe('Token Cell', () => {
let wrapper
const state = {
metamask: {
network: 'test',
currentCurrency: 'usd',
selectedTokenAddress: '0xToken',
selectedAddress: '0xAddress',
contractExchangeRates: {
'0xAnotherToken': 0.015,
},
conversionRate: 7.00,
},
appState: {
sidebar: {
isOpen: true,
},
},
}
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
const store = mockStore(state)
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<TokenCell
address="0xAnotherToken"
symbol="TEST"
string="5.000"
network={22}
currentCurrency="usd"
image="./test-image"
/>
</Provider>
)
})
it('renders Identicon with props from token cell', () => {
assert.equal(wrapper.find(Identicon).prop('address'), '0xAnotherToken')
assert.equal(wrapper.find(Identicon).prop('network'), 'test')
assert.equal(wrapper.find(Identicon).prop('image'), './test-image')
})
it('renders token balance', () => {
assert.equal(wrapper.find('.token-list-item__token-balance').text(), '5.000')
})
it('renders token symbol', () => {
assert.equal(wrapper.find('.token-list-item__token-symbol').text(), 'TEST')
})
it('renders converted fiat amount', () => {
assert.equal(wrapper.find('.token-list-item__fiat-amount').text(), '0.52 USD')
})
})

View File

@ -0,0 +1,43 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { shallow } from 'enzyme'
import Alert from '../index'
describe('Alert', () => {
let wrapper
beforeEach(() => {
wrapper = shallow(
<Alert />
)
})
it('renders nothing with no visible boolean in state', () => {
const alert = wrapper.find('.global-alert')
assert.equal(alert.length, 0)
})
it('renders when visible in state is true, and message', () => {
const errorMessage = 'Error Message'
wrapper.setState({ visible: true, msg: errorMessage })
const alert = wrapper.find('.global-alert')
assert.equal(alert.length, 1)
const errorText = wrapper.find('.msg')
assert.equal(errorText.text(), errorMessage)
})
it('calls component method when componentWillReceiveProps is called', () => {
const animateInSpy = sinon.stub(wrapper.instance(), 'animateIn')
const animateOutSpy = sinon.stub(wrapper.instance(), 'animateOut')
wrapper.setProps({ visible: true })
assert(animateInSpy.calledOnce)
wrapper.setProps({ visible: false })
assert(animateOutSpy.calledOnce)
})
})

View File

@ -0,0 +1,44 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import withTokenTracker from '../with-token-tracker.component'
import TokenBalance from '../../../../components/ui/token-balance/token-balance.component'
// import sinon from 'sinon'
import TokenTracker from 'eth-token-tracker'
const { createTestProviderTools } = require('../../../../../../test/stub/provider')
const provider = createTestProviderTools({ scaffold: {} }).provider
describe('WithTokenTracker HOC', () => {
let wrapper
beforeEach(() => {
const TokenTracker = withTokenTracker(TokenBalance)
wrapper = shallow(
<TokenTracker
userAddress="0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
token={
{
address: 'test',
}
}
/>
)
})
it('#setError', () => {
wrapper.instance().setError('test')
assert.equal(wrapper.props().error, 'test')
})
it('#updateBalance', () => {
wrapper.instance().tracker = new TokenTracker({
provider,
})
wrapper.instance().updateBalance([{ string: 'test string', symbol: 'test symbol' }])
assert.equal(wrapper.props().string, 'test string')
assert.equal(wrapper.props().symbol, 'test symbol')
})
})

View File

@ -0,0 +1,100 @@
import React from 'react'
import { Provider } from 'react-redux'
import assert from 'assert'
import sinon from 'sinon'
import configureMockStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../test/lib/render-helpers'
import AddToken from '../index'
describe('Add Token', () => {
let wrapper
const state = {
metamask: {
tokens: [],
},
}
const mockStore = configureMockStore()
const store = mockStore(state)
const props = {
history: {
push: sinon.stub().callsFake(() => {}),
},
setPendingTokens: sinon.spy(),
clearPendingTokens: sinon.spy(),
tokens: [],
identities: {},
}
before(() => {
wrapper = mountWithRouter(
<Provider store={store}>
<AddToken.WrappedComponent {...props} />
</Provider>, store
)
wrapper.find({ name: 'customToken' }).simulate('click')
})
afterEach(() => {
props.history.push.reset()
})
describe('Add Token', () => {
it('next button is disabled when no fields are populated', () => {
const nextButton = wrapper.find('.button.btn-secondary.page-container__footer-button')
assert.equal(nextButton.props().disabled, true)
})
it('edits token address', () => {
const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'
const event = { target: { value: tokenAddress } }
const customAddress = wrapper.find('input#custom-address')
customAddress.simulate('change', event)
assert.equal(wrapper.find('AddToken').instance().state.customAddress, tokenAddress)
})
it('edits token symbol', () => {
const tokenSymbol = 'META'
const event = { target: { value: tokenSymbol } }
const customAddress = wrapper.find('#custom-symbol')
customAddress.last().simulate('change', event)
assert.equal(wrapper.find('AddToken').instance().state.customSymbol, tokenSymbol)
})
it('edits token decimal precision', () => {
const tokenPrecision = '2'
const event = { target: { value: tokenPrecision } }
const customAddress = wrapper.find('#custom-decimals')
customAddress.last().simulate('change', event)
assert.equal(wrapper.find('AddToken').instance().state.customDecimals, tokenPrecision)
})
it('next', () => {
const nextButton = wrapper.find('.button.btn-secondary.page-container__footer-button')
nextButton.simulate('click')
assert(props.setPendingTokens.calledOnce)
assert(props.history.push.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/confirm-add-token')
})
it('cancels', () => {
const cancelButton = wrapper.find('button.btn-default.page-container__footer-button')
cancelButton.simulate('click')
assert(props.clearPendingTokens.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/')
})
})
})

View File

@ -0,0 +1,46 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mountWithRouter } from '../../../../../test/lib/render-helpers'
import CreateAccountPage from '../index'
describe('Create Account Page', () => {
let wrapper
const props = {
history: {
push: sinon.spy(),
},
location: {
pathname: '/new-account',
},
}
before(() => {
wrapper = mountWithRouter(
<CreateAccountPage {...props} />
)
})
afterEach(() => {
props.history.push.resetHistory()
})
it('clicks create account and routes to new-account path', () => {
const createAccount = wrapper.find('.new-account__tabs__tab').at(0)
createAccount.simulate('click')
assert.equal(props.history.push.getCall(0).args[0], '/new-account')
})
it('clicks import account and routes to import new account path', () => {
const importAccount = wrapper.find('.new-account__tabs__tab').at(1)
importAccount.simulate('click')
assert.equal(props.history.push.getCall(0).args[0], '/new-account/import')
})
it('clicks connect HD Wallet and routes to connect new account path', () => {
const connectHdWallet = wrapper.find('.new-account__tabs__tab').at(2)
connectHdWallet.simulate('click')
assert.equal(props.history.push.getCall(0).args[0], '/new-account/connect')
})
})

View File

@ -0,0 +1,39 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import { DEFAULT_ROUTE } from '../../../../helpers/constants/routes'
import EndOfFlowScreen from '../index'
describe('End of Flow Screen', () => {
let wrapper
const props = {
history: {
push: sinon.spy(),
},
completeOnboarding: sinon.spy(),
}
beforeEach(() => {
wrapper = mountWithRouter(
<EndOfFlowScreen.WrappedComponent {...props} />
)
})
it('renders', () => {
assert.equal(wrapper.length, 1)
})
it('', (done) => {
const endOfFlowButton = wrapper.find('.btn-primary.first-time-flow__button')
endOfFlowButton.simulate('click')
setImmediate(() => {
assert(props.completeOnboarding.calledOnce)
assert(props.history.push.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], DEFAULT_ROUTE)
done()
})
})
})

View File

@ -0,0 +1,73 @@
import React from 'react'
import assert from 'assert'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import {
DEFAULT_ROUTE,
LOCK_ROUTE,
INITIALIZE_WELCOME_ROUTE,
INITIALIZE_UNLOCK_ROUTE,
} from '../../../../helpers/constants/routes'
import FirstTimeFlowSwitch from '../index'
describe('FirstTimeFlowSwitch', () => {
it('redirects to /welcome route with no props', () => {
const wrapper = mountWithRouter(
<FirstTimeFlowSwitch.WrappedComponent />
)
assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: INITIALIZE_WELCOME_ROUTE } }).length, 1)
})
it('redirects to / route when completedOnboarding is true', () => {
const props = {
completedOnboarding: true,
}
const wrapper = mountWithRouter(
<FirstTimeFlowSwitch.WrappedComponent {...props} />
)
assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: DEFAULT_ROUTE } }).length, 1)
})
it('redirects to /lock route when isUnlocked is true ', () => {
const props = {
completedOnboarding: false,
isUnlocked: true,
}
const wrapper = mountWithRouter(
<FirstTimeFlowSwitch.WrappedComponent {...props} />
)
assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: LOCK_ROUTE } }).length, 1)
})
it('redirects to /welcome route when isInitialized is false', () => {
const props = {
completedOnboarding: false,
isUnlocked: false,
isInitialized: false,
}
const wrapper = mountWithRouter(
<FirstTimeFlowSwitch.WrappedComponent {...props} />
)
assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: INITIALIZE_WELCOME_ROUTE } }).length, 1)
})
it('redirects to /unlock route when isInitialized is true', () => {
const props = {
completedOnboarding: false,
isUnlocked: false,
isInitialized: true,
}
const wrapper = mountWithRouter(
<FirstTimeFlowSwitch.WrappedComponent {...props} />
)
assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: INITIALIZE_UNLOCK_ROUTE } }).length, 1)
})
})

View File

@ -0,0 +1,43 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import configureMockStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import MetaMetricsOptIn from '../index'
describe('MetaMetricsOptIn', () => {
let wrapper
const props = {
history: {
push: sinon.spy(),
},
setParticipateInMetaMetrics: sinon.stub().resolves(),
participateInMetaMetrics: false,
}
const mockStore = {
metamask: {},
}
const store = configureMockStore()(mockStore)
beforeEach(() => {
wrapper = mountWithRouter(
<MetaMetricsOptIn.WrappedComponent {...props} />, store
)
})
afterEach(() => {
props.setParticipateInMetaMetrics.resetHistory()
})
it('opt out of metametrics', () => {
const noThanksButton = wrapper.find('.btn-default.page-container__footer-button')
noThanksButton.simulate('click')
assert(props.setParticipateInMetaMetrics.calledOnce)
assert.equal(props.setParticipateInMetaMetrics.getCall(0).args[0], false)
})
})

View File

@ -0,0 +1,48 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import RevealSeedPhrase from '../index'
describe('Reveal Seed Phrase', () => {
let wrapper
const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
const props = {
history: {
push: sinon.spy(),
},
seedPhrase: TEST_SEED,
setSeedPhraseBackedUp: sinon.spy(),
setCompletedOnboarding: sinon.spy(),
}
beforeEach(() => {
wrapper = mount(
<RevealSeedPhrase.WrappedComponent {...props} />, {
context: {
t: str => str,
metricsEvent: () => {},
},
}
)
})
it('seed phrase', () => {
const seedPhrase = wrapper.find('.reveal-seed-phrase__secret-words--hidden')
assert.equal(seedPhrase.length, 1)
assert.equal(seedPhrase.text(), TEST_SEED)
})
it('clicks to reveal', () => {
const reveal = wrapper.find('.reveal-seed-phrase__secret-blocker')
assert.equal(wrapper.state().isShowingSeedPhrase, false)
reveal.simulate('click')
assert.equal(wrapper.state().isShowingSeedPhrase, true)
const showSeed = wrapper.find('.reveal-seed-phrase__secret-words')
assert.equal(showSeed.length, 1)
})
})

View File

@ -0,0 +1,46 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import SelectAction from '../index'
describe('Selection Action', () => {
let wrapper
const props = {
isInitialized: false,
setFirstTimeFlowType: sinon.spy(),
history: {
push: sinon.spy(),
},
}
beforeEach(() => {
wrapper = mountWithRouter(
<SelectAction.WrappedComponent {...props} />
)
})
afterEach(() => {
props.setFirstTimeFlowType.resetHistory()
props.history.push.resetHistory()
})
it('clicks import wallet to route to import FTF', () => {
const importWalletButton = wrapper.find('.btn-primary.first-time-flow__button').at(0)
importWalletButton.simulate('click')
assert(props.setFirstTimeFlowType.calledOnce)
assert.equal(props.setFirstTimeFlowType.getCall(0).args[0], 'import')
assert(props.history.push.calledOnce)
})
it('clicks create wallet to route to create FTF ', () => {
const createWalletButton = wrapper.find('.btn-primary.first-time-flow__button').at(1)
createWalletButton.simulate('click')
assert(props.setFirstTimeFlowType.calledOnce)
assert.equal(props.setFirstTimeFlowType.getCall(0).args[0], 'create')
assert(props.history.push.calledOnce)
})
})

View File

@ -0,0 +1,55 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import configureMockStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import Welcome from '../index'
describe('Welcome', () => {
const mockStore = {
metamask: {},
}
const store = configureMockStore()(mockStore)
after(() => {
sinon.restore()
})
it('routes to select action when participateInMetaMetrics is not initialized', () => {
const props = {
history: {
push: sinon.spy(),
},
}
const wrapper = mountWithRouter(
<Welcome.WrappedComponent {...props} />, store
)
const getStartedButton = wrapper.find('.btn-primary.first-time-flow__button')
getStartedButton.simulate('click')
assert.equal(props.history.push.getCall(0).args[0], '/initialize/select-action')
})
it('routes to correct password when participateInMetaMetrics is initialized', () => {
const props = {
welcomeScreenSeen: true,
participateInMetaMetrics: false,
history: {
push: sinon.spy(),
},
}
const wrapper = mountWithRouter(
<Welcome.WrappedComponent {...props} />, store
)
const getStartedButton = wrapper.find('.btn-primary.first-time-flow__button')
getStartedButton.simulate('click')
assert.equal(props.history.push.getCall(0).args[0], '/initialize/create-password')
})
})

View File

@ -0,0 +1,31 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import RevealSeedPage from '../reveal-seed'
describe('Reveal Seed Page', () => {
let wrapper
const props = {
history: {
push: sinon.spy(),
},
requestRevealSeedWords: sinon.stub().resolves(),
}
beforeEach(() => {
wrapper = mount(
<RevealSeedPage.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
it('form submit', () => {
wrapper.find('form').simulate('submit')
assert(props.requestRevealSeedWords.calledOnce)
})
})

View File

@ -0,0 +1,48 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mountWithRouter } from '../../../../../test/lib/render-helpers'
import Lock from '../index'
describe('Lock', () => {
it('replaces history with default route when isUnlocked false', () => {
const props = {
isUnlocked: false,
history: {
replace: sinon.spy(),
},
}
mountWithRouter(
<Lock.WrappedComponent {...props} />
)
assert.equal(props.history.replace.getCall(0).args[0], '/')
})
it('locks and pushes history with default route when isUnlocked true', (done) => {
const props = {
isUnlocked: true,
lockMetamask: sinon.stub(),
history: {
push: sinon.spy(),
},
}
props.lockMetamask.resolves()
mountWithRouter(
<Lock.WrappedComponent {...props} />
)
assert(props.lockMetamask.calledOnce)
setImmediate(() => {
assert.equal(props.history.push.getCall(0).args[0], '/')
done()
})
})
})

View File

@ -0,0 +1,55 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import SecurityTab from '../index'
describe('Security Tab', () => {
let wrapper
const props = {
revealSeedConfirmation: sinon.spy(),
showClearApprovalModal: sinon.spy(),
setParticipateInMetaMetrics: sinon.spy(),
displayWarning: sinon.spy(),
setShowIncomingTransactionsFeatureFlag: sinon.spy(),
history: {
push: sinon.spy(),
},
privacyMode: true,
warning: '',
participateInMetaMetrics: false,
}
beforeEach(() => {
wrapper = mount(
<SecurityTab.WrappedComponent {...props} />, {
context: {
t: str => str,
metricsEvent: () => {},
},
}
)
})
it('navigates to reveal seed words page', () => {
const seedWords = wrapper.find('.button.btn-danger.btn--large')
seedWords.simulate('click')
assert(props.history.push.calledOnce)
assert.equal(props.history.push.getCall(0).args[0], '/seed')
})
it('toggles incoming txs', () => {
const incomingTxs = wrapper.find({ type: 'checkbox' }).at(0)
incomingTxs.simulate('click')
assert(props.setShowIncomingTransactionsFeatureFlag.calledOnce)
})
it('toggles metaMetrics', () => {
const metaMetrics = wrapper.find({ type: 'checkbox' }).at(1)
metaMetrics.simulate('click')
assert(props.setParticipateInMetaMetrics.calledOnce)
})
})

View File

@ -0,0 +1,61 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import SettingsTab from '../index'
describe('Settings Tab', () => {
let wrapper
const props = {
setCurrentCurrency: sinon.spy(),
displayWarning: sinon.spy(),
setUseBlockie: sinon.spy(),
updateCurrentLocale: sinon.spy(),
setUseNativeCurrencyAsPrimaryCurrencyPreference: sinon.spy(),
warning: '',
currentLocale: 'en',
useBlockie: false,
currentCurrency: 'usd',
conversionDate: 1,
nativeCurrency: 'eth',
useNativeCurrencyAsPrimaryCurrency: true,
}
beforeEach(() => {
wrapper = mount(
<SettingsTab.WrappedComponent {...props} />, {
context: {
t: str => str,
},
}
)
})
it('selects currency', async () => {
const selectCurrency = wrapper.find({ placeholder: 'selectCurrency' })
selectCurrency.props().onSelect('eur')
assert(props.setCurrentCurrency.calledOnce)
})
it('selects locale', async () => {
const selectLocale = wrapper.find({ placeholder: 'selectLocale' })
await selectLocale.props().onSelect('ja')
assert(props.updateCurrentLocale.calledOnce)
})
it('sets fiat primary currency', () => {
const selectFiat = wrapper.find('#fiat-primary-currency')
selectFiat.simulate('change')
assert(props.setUseNativeCurrencyAsPrimaryCurrencyPreference.calledOnce)
})
it('toggles blockies', () => {
const toggleBlockies = wrapper.find({ type: 'checkbox' })
toggleBlockies.simulate('click')
assert(props.setUseBlockie.calledOnce)
})
})

View File

@ -0,0 +1,69 @@
import React from 'react'
import assert from 'assert'
import sinon from 'sinon'
import { mount } from 'enzyme'
import UnlockPage from '../index'
describe('Unlock Page', () => {
let wrapper
const props = {
history: {
push: sinon.spy(),
},
isUnlocked: false,
onImport: sinon.spy(),
onRestore: sinon.spy(),
onSubmit: sinon.spy(),
forceUpdateMetamaskState: sinon.spy(),
showOptInModal: sinon.spy(),
}
beforeEach(() => {
wrapper = mount(
<UnlockPage.WrappedComponent{...props} />, {
context: {
t: str => str,
},
}
)
})
after(() => {
sinon.restore()
})
it('renders', () => {
assert.equal(wrapper.length, 1)
})
it('changes password and submits', () => {
const passwordField = wrapper.find({ type: 'password', id: 'password' })
const loginButton = wrapper.find({ type: 'submit' }).last()
const event = { target: { value: 'password' } }
assert.equal(wrapper.instance().state.password, '')
passwordField.last().simulate('change', event)
assert.equal(wrapper.instance().state.password, 'password')
loginButton.simulate('click')
assert(props.onSubmit.calledOnce)
})
it('clicks imports seed button', () => {
const importSeedButton = wrapper.find('.unlock-page__link--import')
importSeedButton.simulate('click')
assert(props.onImport.calledOnce)
})
it('clicks restore', () => {
const restoreFromSeedButton = wrapper.find('.unlock-page__link').at(0)
restoreFromSeedButton.simulate('click')
assert(props.onRestore.calledOnce)
})
})

View File

@ -94,13 +94,15 @@ export default class UnlockPage extends Component {
this.setState({ password: target.value, error: null })
// tell mascot to look at page action
const element = target
const boundingRect = element.getBoundingClientRect()
const coordinates = getCaretCoordinates(element, element.selectionEnd)
this.animationEventEmitter.emit('point', {
x: boundingRect.left + coordinates.left - element.scrollLeft,
y: boundingRect.top + coordinates.top - element.scrollTop,
})
if (target.getBoundingClientRect) {
const element = target
const boundingRect = element.getBoundingClientRect()
const coordinates = getCaretCoordinates(element, element.selectionEnd)
this.animationEventEmitter.emit('point', {
x: boundingRect.left + coordinates.left - element.scrollLeft,
y: boundingRect.top + coordinates.top - element.scrollTop,
})
}
}
renderSubmitButton () {

View File

@ -0,0 +1,159 @@
import assert from 'assert'
import {
unconfirmedTransactionsCountSelector,
tokenAmountAndToAddressSelector,
approveTokenAmountAndToAddressSelector,
sendTokenTokenAmountAndToAddressSelector,
contractExchangeRateSelector,
} from '../confirm-transaction'
describe('Confirm Transaction Selector', () => {
describe('unconfirmedTransactionsCountSelector', () => {
const state = {
metamask: {
unapprovedTxs: {
1: {
metamaskNetworkId: 'test',
},
2: {
metmaskNetworkId: 'other network',
},
},
unapprovedMsgCount: 1,
unapprovedPersonalMsgCount: 1,
unapprovedTypedMessagesCount: 1,
network: 'test',
},
}
it('returns number of txs in unapprovedTxs state with the same network plus unapproved signing method counts', () => {
assert.equal(unconfirmedTransactionsCountSelector(state), 4)
})
})
describe('tokenAmountAndToAddressSelector', () => {
const state = {
confirmTransaction: {
tokenData: {
name: 'transfer',
params: [
{
name: '_to',
value: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
type: 'address',
},
{
name: '_value',
value: '1',
type: 'uint256',
},
],
},
tokenProps: {
tokenDecimals: '2',
tokenSymbol: 'META',
},
},
}
it('returns calulcated token amount based on token value and token decimals and recipient address', () => {
assert.deepEqual(tokenAmountAndToAddressSelector(state),
{ toAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', tokenAmount: 0.01 })
})
})
describe('approveTokenAmountAndToAddressSelector', () => {
const state = {
confirmTransaction: {
tokenData: {
name: 'approve',
params: [
{
name: '_spender',
value: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
type: 'address',
},
{
name: '_value',
value: '1',
type: 'uint256',
},
],
},
tokenProps: {
tokenDecimals: '2',
tokenSymbol: 'META',
},
},
}
it('returns token amount and recipient for approve token allocation spending', () => {
assert.deepEqual(approveTokenAmountAndToAddressSelector(state),
{ toAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', tokenAmount: 0.01 })
})
})
describe('sendTokenTokenAmountAndToAddressSelector', () => {
const state = {
confirmTransaction: {
tokenData: {
name: 'transfer',
params: [
{
name: '_to',
value: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
type: 'address',
},
{
name: '_value',
value: '1',
type: 'uint256',
},
],
},
tokenProps: {
tokenDecimals: '2',
tokenSymbol: 'META',
},
},
}
it('returns token address and calculated token amount', () => {
assert.deepEqual(sendTokenTokenAmountAndToAddressSelector(state),
{ toAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', tokenAmount: 0.01 })
})
})
describe('contractExchangeRateSelector', () => {
const state = {
metamask: {
contractExchangeRates: {
'0xTokenAddress': '10',
},
},
confirmTransaction: {
txData: {
txParams: {
to: '0xTokenAddress',
},
},
},
}
it('returns contract exchange rate in metamask state based on confirm transaction txParams token recipient', () => {
assert.equal(contractExchangeRateSelector(state), 10)
})
})
})

View File

@ -0,0 +1,28 @@
import assert from 'assert'
import { selectedTokenSelector } from '../tokens'
const metaToken = {
'address': '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4',
'symbol': 'META',
'decimals': 18,
}
const state = {
metamask: {
selectedTokenAddress: '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4',
tokens: [
{
'address': '0x06012c8cf97bead5deae237070f9587f8e7a266d',
'symbol': 'CK',
'decimals': 0,
},
metaToken,
],
},
}
describe('Selected Token Selector', () => {
it('selects token info from tokens based on selectedTokenAddress in state', () => {
const tokenInfo = selectedTokenSelector(state)
assert.equal(tokenInfo, metaToken)
})
})

View File

@ -0,0 +1,356 @@
import assert from 'assert'
import {
unapprovedMessagesSelector,
transactionsSelector,
nonceSortedTransactionsSelector,
nonceSortedPendingTransactionsSelector,
nonceSortedCompletedTransactionsSelector,
submittedPendingTransactionsSelector,
} from '../transactions'
describe('Transaction Selectors', () => {
describe('unapprovedMessagesSelector', () => {
it('returns eth sign msg from unapprovedMsgs', () => {
const msg = {
id: 1,
msgParams: {
from: '0xAddress',
data: '0xData',
origin: 'origin',
},
time: 1,
status: 'unapproved',
type: 'eth_sign',
}
const state = {
metamask: {
unapprovedMsgs: {
1: msg,
},
},
}
const msgSelector = unapprovedMessagesSelector(state)
assert(Array.isArray(msgSelector))
assert.deepEqual(msgSelector, [msg])
})
it('returns personal sign from unapprovedPersonalMsgsSelector', () => {
const msg = {
id: 1,
msgParams: {
from: '0xAddress',
data: '0xData',
origin: 'origin',
},
time: 1,
status: 'unapproved',
type: 'personal_sign',
}
const state = {
metamask: {
unapprovedPersonalMsgs: {
1: msg,
},
},
}
const msgSelector = unapprovedMessagesSelector(state)
assert(Array.isArray(msgSelector))
assert.deepEqual(msgSelector, [msg])
})
it('returns typed message from unapprovedTypedMessagesSelector', () => {
const msg = {
id: 1,
msgParams: {
data: '0xData',
from: '0xAddress',
version: 'V3',
origin: 'origin',
},
time: 1,
status: 'unapproved',
type: 'eth_signTypedData',
}
const state = {
metamask: {
unapprovedTypedMessages: {
1: msg,
},
},
}
const msgSelector = unapprovedMessagesSelector(state)
assert(Array.isArray(msgSelector))
assert.deepEqual(msgSelector, [msg])
})
})
describe('transactionsSelector', () => {
it('selectedAddressTxList', () => {
const state = {
metamask: {
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
{
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
{
id: 1,
time: 1,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
],
},
}
const orderedTxlist = state.metamask.selectedAddressTxList.sort((a, b) => b.time - a.time)
const txSelector = transactionsSelector(state)
assert(Array.isArray(txSelector))
assert.deepEqual(txSelector, orderedTxlist)
})
it('returns token tx from selectedAddressTxList when selectedTokenAddress is valid', () => {
const state = {
metamask: {
featureFlags: {
showIncomingTransactions: false,
},
selectedTokenAddress: '0xToken',
selectedAddressTxList: [
{
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xToken',
},
},
{
id: 1,
time: 1,
txParams: {
from: '0xAddress',
to: '0xToken',
},
},
],
},
}
const orderedTxlist = state.metamask.selectedAddressTxList.sort((a, b) => b.time - a.time)
const txSelector = transactionsSelector(state)
assert(Array.isArray(txSelector))
assert.deepEqual(txSelector, orderedTxlist)
})
})
describe('nonceSortedTransactionsSelector', () => {
it('returns transaction group nonce sorted tx from from selectedTxList wit', () => {
const tx1 = {
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x0',
},
}
const tx2 = {
id: 1,
time: 1,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x1',
},
}
const state = {
metamask: {
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
tx1,
tx2,
],
},
}
const expectedResult = [
{
nonce: '0x0',
transactions: [ tx1 ],
initialTransaction: tx1,
primaryTransaction: tx1,
hasRetried: false,
hasCancelled: false,
},
{
nonce: '0x1',
transactions: [ tx2 ],
initialTransaction: tx2,
primaryTransaction: tx2,
hasRetried: false,
hasCancelled: false,
},
]
assert.deepEqual(nonceSortedTransactionsSelector(state), expectedResult)
})
})
describe('Sorting Transactions Selectors', () => {
const submittedTx = {
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x0',
},
status: 'submitted',
}
const unapprovedTx = {
id: 1,
time: 1,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x1',
},
status: 'unapproved',
}
const approvedTx = {
id: 2,
time: 2,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x2',
},
status: 'approved',
}
const confirmedTx = {
id: 3,
time: 3,
txParams: {
from: '0xAddress',
to: '0xRecipient',
nonce: '0x3',
},
status: 'confirmed',
}
const state = {
metamask: {
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
submittedTx,
unapprovedTx,
approvedTx,
confirmedTx,
],
},
}
it('nonceSortedPendingTransactionsSelector', () => {
const expectedResult = [
{
nonce: submittedTx.txParams.nonce,
transactions: [ submittedTx ],
initialTransaction: submittedTx,
primaryTransaction: submittedTx,
hasRetried: false,
hasCancelled: false,
},
{
nonce: unapprovedTx.txParams.nonce,
transactions: [ unapprovedTx ],
initialTransaction: unapprovedTx,
primaryTransaction: unapprovedTx,
hasRetried: false,
hasCancelled: false,
},
{
nonce: approvedTx.txParams.nonce,
transactions: [ approvedTx ],
initialTransaction: approvedTx,
primaryTransaction: approvedTx,
hasRetried: false,
hasCancelled: false,
},
]
assert.deepEqual(nonceSortedPendingTransactionsSelector(state), expectedResult)
})
it('nonceSortedCompletedTransactionsSelector', () => {
const expectedResult = [
{
nonce: confirmedTx.txParams.nonce,
transactions: [ confirmedTx ],
initialTransaction: confirmedTx,
primaryTransaction: confirmedTx,
hasRetried: false,
hasCancelled: false,
},
]
assert.deepEqual(nonceSortedCompletedTransactionsSelector(state), expectedResult)
})
it('submittedPendingTransactionsSelector', () => {
const expectedResult = [ submittedTx ]
assert.deepEqual(submittedPendingTransactionsSelector(state), expectedResult)
})
})
})