From 4f7f61c31ec419987dec751a5e5cf72ba1718fa7 Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Thu, 30 Jan 2020 11:34:45 -0800 Subject: [PATCH] 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 --- test/helper.js | 3 + test/lib/render-helpers.js | 44 ++- .../account-menu/account-menu.component.js | 6 + .../account-menu/tests/account-menu.test.js | 209 ++++++++++ .../app/app-header/tests/app-header.test.js | 99 +++++ .../app/dropdowns/account-details-dropdown.js | 2 +- .../advanced-gas-input-component.test.js | 104 +++++ .../app/info-box/tests/info-box.test.js | 37 ++ .../app/menu-bar/tests/menu-bar.test.js | 94 +++++ .../tests/confirm-delete-network.test.js | 58 +++ .../tests/confirm-remove-account.test.js | 82 ++++ .../tests/confirm-reset-account.test.js | 46 +++ .../tests/metametrics-opt-in-modal.test.js | 55 +++ .../tests/reject-transactions.test.js | 48 +++ .../tests/account-details-modal.test.js | 82 ++++ .../tests/transaction-confirmed.test.js | 32 ++ .../app/tests/signature-request.test.js | 69 ++++ .../components/app/tests/token-cell.spec.js | 69 ++++ .../components/ui/alert/tests/alert.test.js | 43 +++ .../with-token-tracker.component.test.js | 44 +++ .../pages/add-token/tests/add-token.test.js | 100 +++++ .../tests/create-account.test.js | 46 +++ .../end-of-flow/tests/end-of-flow.test.js | 39 ++ .../tests/first-time-flow-switch.test.js | 73 ++++ .../tests/metametrics-opt-in.test.js | 43 +++ .../tests/reveal-seed-phrase.test.js | 48 +++ .../select-action/tests/select-action.test.js | 46 +++ .../welcome/tests/welcome.test.js | 55 +++ .../pages/keychains/tests/reveal-seed.test.js | 31 ++ ui/app/pages/lock/tests/lock.test.js | 48 +++ .../gas-fee-display.component.test.js | 0 .../security-tab/tests/security-tab.test.js | 55 +++ .../settings-tab/tests/settings-tab.test.js | 61 +++ .../unlock-page/tests/unlock-page.test.js | 69 ++++ .../unlock-page/unlock-page.component.js | 16 +- .../tests/confirm-transaction.test.js | 159 ++++++++ ui/app/selectors/tests/tokens.test.js | 28 ++ ui/app/selectors/tests/transactions.test.js | 356 ++++++++++++++++++ 38 files changed, 2475 insertions(+), 24 deletions(-) create mode 100644 ui/app/components/app/account-menu/tests/account-menu.test.js create mode 100644 ui/app/components/app/app-header/tests/app-header.test.js create mode 100644 ui/app/components/app/gas-customization/advanced-gas-inputs/tests/advanced-gas-input-component.test.js create mode 100644 ui/app/components/app/info-box/tests/info-box.test.js create mode 100644 ui/app/components/app/menu-bar/tests/menu-bar.test.js create mode 100644 ui/app/components/app/modals/confirm-delete-network/tests/confirm-delete-network.test.js create mode 100644 ui/app/components/app/modals/confirm-remove-account/tests/confirm-remove-account.test.js create mode 100644 ui/app/components/app/modals/confirm-reset-account/tests/confirm-reset-account.test.js create mode 100644 ui/app/components/app/modals/metametrics-opt-in-modal/tests/metametrics-opt-in-modal.test.js create mode 100644 ui/app/components/app/modals/reject-transactions/tests/reject-transactions.test.js create mode 100644 ui/app/components/app/modals/tests/account-details-modal.test.js create mode 100644 ui/app/components/app/modals/transaction-confirmed/tests/transaction-confirmed.test.js create mode 100644 ui/app/components/app/tests/signature-request.test.js create mode 100644 ui/app/components/app/tests/token-cell.spec.js create mode 100644 ui/app/components/ui/alert/tests/alert.test.js create mode 100644 ui/app/helpers/higher-order-components/with-token-tracker/tests/with-token-tracker.component.test.js create mode 100644 ui/app/pages/add-token/tests/add-token.test.js create mode 100644 ui/app/pages/create-account/tests/create-account.test.js create mode 100644 ui/app/pages/first-time-flow/end-of-flow/tests/end-of-flow.test.js create mode 100644 ui/app/pages/first-time-flow/first-time-flow-switch/tests/first-time-flow-switch.test.js create mode 100644 ui/app/pages/first-time-flow/metametrics-opt-in/tests/metametrics-opt-in.test.js create mode 100644 ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/tests/reveal-seed-phrase.test.js create mode 100644 ui/app/pages/first-time-flow/select-action/tests/select-action.test.js create mode 100644 ui/app/pages/first-time-flow/welcome/tests/welcome.test.js create mode 100644 ui/app/pages/keychains/tests/reveal-seed.test.js create mode 100644 ui/app/pages/lock/tests/lock.test.js rename ui/app/pages/send/send-content/send-gas-row/gas-fee-display/{test => tests}/gas-fee-display.component.test.js (100%) create mode 100644 ui/app/pages/settings/security-tab/tests/security-tab.test.js create mode 100644 ui/app/pages/settings/settings-tab/tests/settings-tab.test.js create mode 100644 ui/app/pages/unlock-page/tests/unlock-page.test.js create mode 100644 ui/app/selectors/tests/confirm-transaction.test.js create mode 100644 ui/app/selectors/tests/tokens.test.js create mode 100644 ui/app/selectors/tests/transactions.test.js diff --git a/test/helper.js b/test/helper.js index 25db91f35de9..ce47effbb205 100644 --- a/test/helper.js +++ b/test/helper.js @@ -73,6 +73,9 @@ require('jsdom-global')() // localStorage window.localStorage = {} +// override metamask-logo +window.requestAnimationFrame = () => {} + // crypto.getRandomValues if (!window.crypto) { window.crypto = {} diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index 4a0a3413d8d2..45bcb1656257 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -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,22 +10,41 @@ 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 = () => {node} + const Wrapper = () => ( + + {component} + + ) return mount(, createContext()) } diff --git a/ui/app/components/app/account-menu/account-menu.component.js b/ui/app/components/app/account-menu/account-menu.component.js index 0c027805196c..f028f93e7f91 100644 --- a/ui/app/components/app/account-menu/account-menu.component.js +++ b/ui/app/components/app/account-menu/account-menu.component.js @@ -195,6 +195,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' diff --git a/ui/app/components/app/account-menu/tests/account-menu.test.js b/ui/app/components/app/account-menu/tests/account-menu.test.js new file mode 100644 index 000000000000..b1ebcf61c902 --- /dev/null +++ b/ui/app/components/app/account-menu/tests/account-menu.test.js @@ -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( + + + , 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') + }) + }) +}) diff --git a/ui/app/components/app/app-header/tests/app-header.test.js b/ui/app/components/app/app-header/tests/app-header.test.js new file mode 100644 index 000000000000..37d1c32c43ed --- /dev/null +++ b/ui/app/components/app/app-header/tests/app-header.test.js @@ -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( + , { + 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) + }) + }) + +}) diff --git a/ui/app/components/app/dropdowns/account-details-dropdown.js b/ui/app/components/app/dropdowns/account-details-dropdown.js index 086526a100e3..68644a30499b 100644 --- a/ui/app/components/app/dropdowns/account-details-dropdown.js +++ b/ui/app/components/app/dropdowns/account-details-dropdown.js @@ -47,7 +47,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, diff --git a/ui/app/components/app/gas-customization/advanced-gas-inputs/tests/advanced-gas-input-component.test.js b/ui/app/components/app/gas-customization/advanced-gas-inputs/tests/advanced-gas-input-component.test.js new file mode 100644 index 000000000000..e45740f5b302 --- /dev/null +++ b/ui/app/components/app/gas-customization/advanced-gas-inputs/tests/advanced-gas-input-component.test.js @@ -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( + , { + 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') + }) +}) diff --git a/ui/app/components/app/info-box/tests/info-box.test.js b/ui/app/components/app/info-box/tests/info-box.test.js new file mode 100644 index 000000000000..904e868baa0d --- /dev/null +++ b/ui/app/components/app/info-box/tests/info-box.test.js @@ -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() + }) + + 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) + }) +}) diff --git a/ui/app/components/app/menu-bar/tests/menu-bar.test.js b/ui/app/components/app/menu-bar/tests/menu-bar.test.js new file mode 100644 index 000000000000..a43b994baa61 --- /dev/null +++ b/ui/app/components/app/menu-bar/tests/menu-bar.test.js @@ -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( + + + , 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( + + + , 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) + }) +}) diff --git a/ui/app/components/app/modals/confirm-delete-network/tests/confirm-delete-network.test.js b/ui/app/components/app/modals/confirm-delete-network/tests/confirm-delete-network.test.js new file mode 100644 index 000000000000..ce0d49013257 --- /dev/null +++ b/ui/app/components/app/modals/confirm-delete-network/tests/confirm-delete-network.test.js @@ -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( + , { + 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) + }) + }) + +}) diff --git a/ui/app/components/app/modals/confirm-remove-account/tests/confirm-remove-account.test.js b/ui/app/components/app/modals/confirm-remove-account/tests/confirm-remove-account.test.js new file mode 100644 index 000000000000..0a6eb828d023 --- /dev/null +++ b/ui/app/components/app/modals/confirm-remove-account/tests/confirm-remove-account.test.js @@ -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( + + + , { + 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) + }) +}) diff --git a/ui/app/components/app/modals/confirm-reset-account/tests/confirm-reset-account.test.js b/ui/app/components/app/modals/confirm-reset-account/tests/confirm-reset-account.test.js new file mode 100644 index 000000000000..175a38158ca8 --- /dev/null +++ b/ui/app/components/app/modals/confirm-reset-account/tests/confirm-reset-account.test.js @@ -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( + , { + 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() + }) + }) +}) diff --git a/ui/app/components/app/modals/metametrics-opt-in-modal/tests/metametrics-opt-in-modal.test.js b/ui/app/components/app/modals/metametrics-opt-in-modal/tests/metametrics-opt-in-modal.test.js new file mode 100644 index 000000000000..2bae8f9b1972 --- /dev/null +++ b/ui/app/components/app/modals/metametrics-opt-in-modal/tests/metametrics-opt-in-modal.test.js @@ -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( + , { + 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() + }) + }) + +}) diff --git a/ui/app/components/app/modals/reject-transactions/tests/reject-transactions.test.js b/ui/app/components/app/modals/reject-transactions/tests/reject-transactions.test.js new file mode 100644 index 000000000000..505e9a05ec54 --- /dev/null +++ b/ui/app/components/app/modals/reject-transactions/tests/reject-transactions.test.js @@ -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( + , { + 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() + }) + + }) +}) diff --git a/ui/app/components/app/modals/tests/account-details-modal.test.js b/ui/app/components/app/modals/tests/account-details-modal.test.js new file mode 100644 index 000000000000..24fe0eee40bd --- /dev/null +++ b/ui/app/components/app/modals/tests/account-details-modal.test.js @@ -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( + , { + 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(), '') + }) +}) diff --git a/ui/app/components/app/modals/transaction-confirmed/tests/transaction-confirmed.test.js b/ui/app/components/app/modals/transaction-confirmed/tests/transaction-confirmed.test.js new file mode 100644 index 000000000000..7d4debfd7738 --- /dev/null +++ b/ui/app/components/app/modals/transaction-confirmed/tests/transaction-confirmed.test.js @@ -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( + , { + 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) + }) +}) diff --git a/ui/app/components/app/tests/signature-request.test.js b/ui/app/components/app/tests/signature-request.test.js new file mode 100644 index 000000000000..eda054c168ac --- /dev/null +++ b/ui/app/components/app/tests/signature-request.test.js @@ -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( + + + , 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) + }) + +}) diff --git a/ui/app/components/app/tests/token-cell.spec.js b/ui/app/components/app/tests/token-cell.spec.js new file mode 100644 index 000000000000..877713e4ce55 --- /dev/null +++ b/ui/app/components/app/tests/token-cell.spec.js @@ -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( + + + + ) + }) + + 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') + }) + +}) diff --git a/ui/app/components/ui/alert/tests/alert.test.js b/ui/app/components/ui/alert/tests/alert.test.js new file mode 100644 index 000000000000..d3de51bfb0a6 --- /dev/null +++ b/ui/app/components/ui/alert/tests/alert.test.js @@ -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( + + ) + }) + + 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) + }) +}) diff --git a/ui/app/helpers/higher-order-components/with-token-tracker/tests/with-token-tracker.component.test.js b/ui/app/helpers/higher-order-components/with-token-tracker/tests/with-token-tracker.component.test.js new file mode 100644 index 000000000000..c7d22242dc49 --- /dev/null +++ b/ui/app/helpers/higher-order-components/with-token-tracker/tests/with-token-tracker.component.test.js @@ -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( + + ) + }) + + 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') + }) + +}) diff --git a/ui/app/pages/add-token/tests/add-token.test.js b/ui/app/pages/add-token/tests/add-token.test.js new file mode 100644 index 000000000000..67465752bc37 --- /dev/null +++ b/ui/app/pages/add-token/tests/add-token.test.js @@ -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( + + + , 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], '/') + }) + }) + +}) diff --git a/ui/app/pages/create-account/tests/create-account.test.js b/ui/app/pages/create-account/tests/create-account.test.js new file mode 100644 index 000000000000..b68a2241e2cf --- /dev/null +++ b/ui/app/pages/create-account/tests/create-account.test.js @@ -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( + + ) + }) + + 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') + }) +}) diff --git a/ui/app/pages/first-time-flow/end-of-flow/tests/end-of-flow.test.js b/ui/app/pages/first-time-flow/end-of-flow/tests/end-of-flow.test.js new file mode 100644 index 000000000000..23cebc68c1f2 --- /dev/null +++ b/ui/app/pages/first-time-flow/end-of-flow/tests/end-of-flow.test.js @@ -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( + + ) + }) + + 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() + }) + }) +}) diff --git a/ui/app/pages/first-time-flow/first-time-flow-switch/tests/first-time-flow-switch.test.js b/ui/app/pages/first-time-flow/first-time-flow-switch/tests/first-time-flow-switch.test.js new file mode 100644 index 000000000000..4c2b60727048 --- /dev/null +++ b/ui/app/pages/first-time-flow/first-time-flow-switch/tests/first-time-flow-switch.test.js @@ -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( + + ) + 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( + + ) + + 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( + + ) + + 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( + + ) + + 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( + + ) + + assert.equal(wrapper.find('Lifecycle').find({ to: { pathname: INITIALIZE_UNLOCK_ROUTE } }).length, 1) + }) + +}) diff --git a/ui/app/pages/first-time-flow/metametrics-opt-in/tests/metametrics-opt-in.test.js b/ui/app/pages/first-time-flow/metametrics-opt-in/tests/metametrics-opt-in.test.js new file mode 100644 index 000000000000..3d70debdaedd --- /dev/null +++ b/ui/app/pages/first-time-flow/metametrics-opt-in/tests/metametrics-opt-in.test.js @@ -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( + , 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) + }) + +}) diff --git a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/tests/reveal-seed-phrase.test.js b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/tests/reveal-seed-phrase.test.js new file mode 100644 index 000000000000..41d170b5d1b0 --- /dev/null +++ b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/tests/reveal-seed-phrase.test.js @@ -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( + , { + 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) + }) +}) diff --git a/ui/app/pages/first-time-flow/select-action/tests/select-action.test.js b/ui/app/pages/first-time-flow/select-action/tests/select-action.test.js new file mode 100644 index 000000000000..fe88c9a0bab7 --- /dev/null +++ b/ui/app/pages/first-time-flow/select-action/tests/select-action.test.js @@ -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( + + ) + }) + + 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) + }) +}) diff --git a/ui/app/pages/first-time-flow/welcome/tests/welcome.test.js b/ui/app/pages/first-time-flow/welcome/tests/welcome.test.js new file mode 100644 index 000000000000..3649cef11b55 --- /dev/null +++ b/ui/app/pages/first-time-flow/welcome/tests/welcome.test.js @@ -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( + , 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( + , 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') + }) +}) diff --git a/ui/app/pages/keychains/tests/reveal-seed.test.js b/ui/app/pages/keychains/tests/reveal-seed.test.js new file mode 100644 index 000000000000..f63c5ae03d6f --- /dev/null +++ b/ui/app/pages/keychains/tests/reveal-seed.test.js @@ -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( + , { + context: { + t: str => str, + }, + } + ) + }) + + it('form submit', () => { + wrapper.find('form').simulate('submit') + assert(props.requestRevealSeedWords.calledOnce) + }) +}) diff --git a/ui/app/pages/lock/tests/lock.test.js b/ui/app/pages/lock/tests/lock.test.js new file mode 100644 index 000000000000..ba412ebf8a56 --- /dev/null +++ b/ui/app/pages/lock/tests/lock.test.js @@ -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( + + ) + + 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( + + ) + + assert(props.lockMetamask.calledOnce) + setImmediate(() => { + assert.equal(props.history.push.getCall(0).args[0], '/') + done() + }) + }) +}) diff --git a/ui/app/pages/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/pages/send/send-content/send-gas-row/gas-fee-display/tests/gas-fee-display.component.test.js similarity index 100% rename from ui/app/pages/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js rename to ui/app/pages/send/send-content/send-gas-row/gas-fee-display/tests/gas-fee-display.component.test.js diff --git a/ui/app/pages/settings/security-tab/tests/security-tab.test.js b/ui/app/pages/settings/security-tab/tests/security-tab.test.js new file mode 100644 index 000000000000..51080cb54ea8 --- /dev/null +++ b/ui/app/pages/settings/security-tab/tests/security-tab.test.js @@ -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( + , { + 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) + }) +}) diff --git a/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js b/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js new file mode 100644 index 000000000000..6481b908dc9f --- /dev/null +++ b/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js @@ -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( + , { + 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) + }) +}) diff --git a/ui/app/pages/unlock-page/tests/unlock-page.test.js b/ui/app/pages/unlock-page/tests/unlock-page.test.js new file mode 100644 index 000000000000..8b1e05729f02 --- /dev/null +++ b/ui/app/pages/unlock-page/tests/unlock-page.test.js @@ -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( + , { + 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) + }) +}) diff --git a/ui/app/pages/unlock-page/unlock-page.component.js b/ui/app/pages/unlock-page/unlock-page.component.js index 993c42d0e557..5b216ac32fcc 100644 --- a/ui/app/pages/unlock-page/unlock-page.component.js +++ b/ui/app/pages/unlock-page/unlock-page.component.js @@ -97,13 +97,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 () { diff --git a/ui/app/selectors/tests/confirm-transaction.test.js b/ui/app/selectors/tests/confirm-transaction.test.js new file mode 100644 index 000000000000..db8772868002 --- /dev/null +++ b/ui/app/selectors/tests/confirm-transaction.test.js @@ -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) + }) + + }) +}) + diff --git a/ui/app/selectors/tests/tokens.test.js b/ui/app/selectors/tests/tokens.test.js new file mode 100644 index 000000000000..d058f4a05148 --- /dev/null +++ b/ui/app/selectors/tests/tokens.test.js @@ -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) + }) +}) diff --git a/ui/app/selectors/tests/transactions.test.js b/ui/app/selectors/tests/transactions.test.js new file mode 100644 index 000000000000..3ab1be47c59c --- /dev/null +++ b/ui/app/selectors/tests/transactions.test.js @@ -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) + + }) + + }) + +})