Merge branch 'develop' into trezor-ledger

This commit is contained in:
Victor Baranov 2019-01-28 23:40:00 +03:00 committed by GitHub
commit 5145fd0f3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3672 additions and 3107 deletions

View File

@ -24,10 +24,10 @@ workflows:
requires:
- prep-deps-npm
- prep-build
- test-e2e-firefox:
requires:
- prep-deps-npm
- prep-build
# - test-e2e-firefox:
# requires:
# - prep-deps-npm
# - prep-build
- test-unit:
requires:
- prep-deps-npm
@ -52,7 +52,7 @@ workflows:
- test-lint
- test-unit
- test-e2e-chrome
- test-e2e-firefox
# - test-e2e-firefox
- test-integration-mascara-chrome
- test-integration-mascara-firefox
- test-integration-flat-chrome

View File

@ -52,6 +52,7 @@ const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
const { importTypes } = require('../../old-ui/app/accounts/import/enums')
const { LEDGER, TREZOR } = require('../../old-ui/app/components/connect-hardware/enum')
const accountsPerPage = 5
@ -381,6 +382,7 @@ module.exports = class MetamaskController extends EventEmitter {
resetAccount: nodeify(this.resetAccount, this),
changePassword: nodeify(this.changePassword, this),
removeAccount: nodeify(this.removeAccount, this),
updateABI: nodeify(this.updateABI, this),
getContract: nodeify(this.getContract, this),
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
@ -923,6 +925,7 @@ module.exports = class MetamaskController extends EventEmitter {
* Removes an account from state / storage.
*
* @param {string[]} address A hex address
* @param {int} network ID
*
*/
async removeAccount (address, network) {
@ -940,6 +943,23 @@ module.exports = class MetamaskController extends EventEmitter {
return address
}
/**
* Updates implementation ABI for proxy account type.
*
* @param {string[]} address A hex address
* @param {int} network ID
*
*/
async updateABI (address, network, newABI) {
// Sets new ABI for implementation contract
try {
await this.keyringController.updateABI(address, network, newABI)
} catch (e) {
log.error(e)
}
return
}
/**
* Imports an account with the specified import strategy.
@ -952,7 +972,8 @@ module.exports = class MetamaskController extends EventEmitter {
*/
async importAccountWithStrategy (strategy, args) {
let keyring
if (strategy === 'Contract') {
if (strategy === importTypes.CONTRACT.DEFAULT || strategy === importTypes.CONTRACT.PROXY) {
args.contractType = strategy
keyring = await this.keyringController.addNewKeyring('Simple Address', args)
} else {
const privateKey = await accountImporter.importAccount(strategy, args)

View File

@ -4,7 +4,9 @@ import { connect } from 'react-redux'
import actions from '../../../../ui/app/actions'
import Web3 from 'web3'
import log from 'loglevel'
import copyToClipboard from 'copy-to-clipboard'
import CopyButton from '../../components/copyButton'
import ErrorComponent from '../../components/error'
import { getFullABI } from './helpers'
class ContractImportView extends Component {
constructor (props) {
@ -23,6 +25,7 @@ class ContractImportView extends Component {
static propTypes = {
error: PropTypes.string,
network: PropTypes.string,
type: PropTypes.string,
displayWarning: PropTypes.func,
importNewAccount: PropTypes.func,
hideWarning: PropTypes.func,
@ -41,9 +44,9 @@ class ContractImportView extends Component {
abiOnChange (abi) {
this.props.hideWarning()
try {
if (abi && JSON.parse(abi)) {
if (abi) {
this.setState({
abi,
abi: JSON.stringify(abi),
abiInputDisabled: true,
importDisabled: false,
})
@ -54,6 +57,12 @@ class ContractImportView extends Component {
}
}
componentDidUpdate (prevProps) {
if (this.props.type !== prevProps.type) {
this.clearInputs()
}
}
render () {
const { error } = this.props
@ -64,6 +73,7 @@ class ContractImportView extends Component {
alignItems: 'center',
padding: '5px 0px 0px 0px',
}}>
<ErrorComponent error={error} />
<span>Paste address of contract here</span>
<input
className="large-input"
@ -76,10 +86,12 @@ class ContractImportView extends Component {
}}
/>
<span style={{ marginTop: '20px' }}>Paste ABI of contract here
<i
className="clipboard cursor-pointer"
style={{ marginLeft: '10px' }}
onClick={(e) => { copyToClipboard(this.state.abi) }}
<CopyButton
value={this.state.abi}
style={{
display: 'inline-block',
}}
tooltipPosition="right"
/>
</span>
<textarea
@ -106,26 +118,17 @@ class ContractImportView extends Component {
autodetectContractAbi = () => {
const { contractAddr, web3 } = this.state
const { type, network } = this.props
if (!contractAddr || !web3.isAddress(contractAddr)) {
this.clearAbi()
return
}
const networkName = this.getBlockscoutApiNetworkSuffix()
const bloscoutApiLink = `https://blockscout.com/poa/${networkName}/api`
const bloscoutApiContractPath = '?module=contract'
const blockscoutApiGetAbiPath = `&action=getabi&address=${this.state.contractAddr}`
const apiLink = `${bloscoutApiLink}${bloscoutApiContractPath}${blockscoutApiGetAbiPath}`
fetch(apiLink)
.then(response => {
return response.json()
})
.then(responseJson => {
this.abiOnChange(responseJson && responseJson.result)
})
.catch((e) => {
getFullABI(web3.eth, contractAddr, network, type)
.then(finalABI => this.abiOnChange(finalABI))
.catch(e => {
this.clearAbi()
log.debug(e)
this.props.displayWarning(e.message)
})
}
@ -178,31 +181,18 @@ class ContractImportView extends Component {
return this.props.displayWarning('Invalid contract ABI')
}
this.props.importNewAccount('Contract', { addr: contractAddr, network: this.props.network, abi })
this.props.importNewAccount(this.props.type, { addr: contractAddr, network: this.props.network, abi })
// JS runtime requires caught rejections but failures are handled by Redux
.catch()
}
getBlockscoutApiNetworkSuffix () {
const { network } = this.props
switch (Number(network)) {
case 1:
return 'mainnet'
case 99:
return 'core'
case 77:
return 'sokol'
case 100:
return 'dai'
case 42:
return 'kovan'
case 3:
return 'ropsten'
case 4:
return 'rinkeby'
default:
return ''
}
clearInputs () {
this.setState({
contractAddr: '',
abi: '',
abiInputDisabled: false,
importDisabled: true,
})
}
clearAbi () {

View File

@ -0,0 +1,20 @@
const importTypes = {
PRIVATE_KEY: 'Private Key',
JSON_FILE: 'JSON File',
CONTRACT: {
DEFAULT: 'Contract',
PROXY: 'Proxy',
},
}
const labels = {
CONTRACT: 'CONTRACT',
PROXY: 'PROXY',
HARDWARE: 'HARDWARE',
IMPORTED: 'IMPORTED',
}
module.exports = {
importTypes,
labels,
}

View File

@ -0,0 +1,98 @@
import log from 'loglevel'
import { importTypes } from './enums'
const nestedJsonObjToArray = (jsonObj) => {
return jsonObjToArray(jsonObj)
}
const jsonObjToArray = (jsonObj) => {
return Object.keys(jsonObj).reduce((arr, key) => {
if (jsonObj[key].constructor === Object || jsonObj[key].constructor === Array) {
arr = arr.concat(jsonObjToArray(jsonObj[key]))
} else if (jsonObj[key].constructor === String) {
arr.push(jsonObj[key])
}
return arr
}, [])
}
const getBlockscoutApiNetworkSuffix = (network) => {
switch (Number(network)) {
case 1:
return 'mainnet'
case 99:
return 'core'
case 77:
return 'sokol'
case 100:
return 'dai'
case 42:
return 'kovan'
case 3:
return 'ropsten'
case 4:
return 'rinkeby'
default:
return ''
}
}
const fetchABI = (addr, network) => {
return new Promise((resolve, reject) => {
const networkName = getBlockscoutApiNetworkSuffix(network)
const bloscoutApiLink = `https://blockscout.com/poa/${networkName}/api`
const bloscoutApiContractPath = '?module=contract'
const blockscoutApiGetAbiPath = `&action=getabi&address=${addr}`
const apiLink = `${bloscoutApiLink}${bloscoutApiContractPath}${blockscoutApiGetAbiPath}`
fetch(apiLink)
.then(response => {
return response.json()
})
.then(responseJson => {
resolve(responseJson && responseJson.result)
})
.catch((e) => {
log.debug(e)
resolve()
})
})
}
const getFullABI = (eth, contractAddr, network, type) => {
return new Promise((resolve, reject) => {
fetchABI(contractAddr, network)
.then((targetABI) => {
targetABI = targetABI && JSON.parse(targetABI)
let finalABI = targetABI
if (type === importTypes.CONTRACT.PROXY) {
if (!eth.contract(targetABI).at(contractAddr).implementation) {
const e = {
message: 'This is not a valid Delegate Proxy contract',
}
reject(e)
}
try {
eth.contract(targetABI).at(contractAddr).implementation.call((err, implAddr) => {
fetchABI(implAddr, network)
.then((implABI) => {
implABI = implABI && JSON.parse(implABI)
finalABI = implABI ? targetABI.concat(implABI) : targetABI
resolve(finalABI)
})
.catch(e => reject(e))
})
} catch (e) {
reject(e)
}
} else {
resolve(finalABI)
}
})
.catch(e => { reject(e) })
})
}
module.exports = {
nestedJsonObjToArray,
getFullABI,
}

View File

@ -1,137 +1,154 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../../../ui/app/actions')
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import actions from '../../../../ui/app/actions'
import Select from 'react-select'
import { importTypes } from './enums'
import { nestedJsonObjToArray } from './helpers'
// Subviews
const JsonImportView = require('./json.js')
const PrivateKeyImportView = require('./private-key.js')
const ContractImportView = require('./contract.js')
import JsonImportView from './json.js'
import PrivateKeyImportView from './private-key.js'
import ContractImportView from './contract.js'
const menuItems = [
'Private Key',
'JSON File',
'Contract',
]
const menuItems = nestedJsonObjToArray(importTypes)
module.exports = connect(mapStateToProps)(AccountImportSubview)
class AccountImportSubview extends Component {
constructor (props) {
super(props)
this.state = {
description: '',
type: importTypes.PRIVATE_KEY,
}
}
static propTypes = {
menuItems: PropTypes.array,
warning: PropTypes.node,
goHome: PropTypes.func,
displayWarning: PropTypes.func,
showImportPage: PropTypes.func,
}
render () {
const props = this.props
const state = this.state || {}
const { menuItems } = props
const { type } = state
function mapStateToProps (state) {
return (
<div style={{
width: '100%',
}}>
<div className="section-title" style={{
height: '1px',
width: '100%',
}} />
<div style={{
width: '100%',
padding: '0 30px',
}}>
<div className="flex-row flex-center">
<div
className="i fa fa-arrow-left fa-lg cursor-pointer"
onClick={(event) => { props.goHome() }}
style={{
position: 'absolute',
left: '30px',
}}
/>
<h2 className="page-subtitle" style={{
fontFamily: 'Nunito SemiBold',
}}
>Import Accounts</h2>
</div>
<div
className="error"
style={{
display: 'inline-block',
alignItems: 'center',
}}>
<span>Imported accounts will not be associated with your originally created Nifty Wallet account seedphrase.</span>
</div>
<div style={{ padding: '10px 0' }}>
<h3 style={{ padding: '3px' }}>Select Type</h3>
<Select {...{
name: 'import-type-select',
clearable: false,
value: type || menuItems[0],
options: menuItems.map((type) => {
return {
value: type,
label: type,
}
}),
onChange: (opt) => { this.onChange(opt) },
}}/>
<p className="hw-connect__header__msg" dangerouslySetInnerHTML={{__html: this.state.description}} />
</div>
{this.renderImportView()}
</div>
</div>
)
}
onChange (opt) {
const props = this.props
props.showImportPage()
const type = opt.value
let description
switch (type) {
case importTypes.PRIVATE_KEY:
case importTypes.JSON_FILE:
description = ''
break
case importTypes.CONTRACT.DEFAULT:
description = `Contract type will automatically retrieve its ABI, if it was verified in <a href='https://blockscout.com' target='_blank'>Blockscout</a>`
break
case importTypes.CONTRACT.PROXY:
description = `Proxy contract type will automatically contain ABI of implementation, if proxy and implementation were both verified in <a href='https://blockscout.com' target='_blank'>Blockscout</a>`
break
default:
description = ''
break
}
this.setState({ type, description })
}
componentWillUnmount () {
this.props.displayWarning('')
}
renderImportView () {
const { menuItems } = this.props
const state = this.state || {}
const { type } = state
const current = type || menuItems[0]
switch (current) {
case importTypes.PRIVATE_KEY:
return <PrivateKeyImportView/>
case importTypes.JSON_FILE:
return <JsonImportView/>
case importTypes.CONTRACT.DEFAULT:
return <ContractImportView type={importTypes.CONTRACT.DEFAULT}/>
case importTypes.CONTRACT.PROXY:
return <ContractImportView type={importTypes.CONTRACT.PROXY}/>
default:
return <JsonImportView/>
}
}
}
const mapStateToProps = (state) => {
return {
menuItems,
}
}
inherits(AccountImportSubview, Component)
function AccountImportSubview () {
Component.call(this)
}
AccountImportSubview.prototype.render = function () {
const props = this.props
const state = this.state || {}
const { menuItems } = props
const { type } = state
return (
h('div', {
style: {
width: '100%',
},
}, [
h('.section-title', { style: {
height: '1px',
width: '100%',
}}),
h('div', {
style: {
width: '100%',
paddingLeft: '30px',
paddingRight: '30px',
},
}, [
h('.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: (event) => {
props.dispatch(actions.goHome())
},
style: {
position: 'absolute',
left: '30px',
},
}),
h('h2.page-subtitle', {
style: {
fontFamily: 'Nunito SemiBold',
},
}, 'Import Accounts'),
]),
h('.error', {
style: {
display: 'inline-block',
alignItems: 'center',
},
}, [
h('span', 'Imported accounts will not be associated with your originally created Nifty Wallet account seedphrase.'),
]),
h('div', {
style: {
padding: '10px 0',
},
}, [
h('h3', { style: { padding: '3px' } }, 'Select Type'),
h('style', `
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
color: rgb(174,174,174);
}
`),
h(Select, {
name: 'import-type-select',
clearable: false,
value: type || menuItems[0],
options: menuItems.map((type) => {
return {
value: type,
label: type,
}
}),
onChange: (opt) => {
props.dispatch(actions.showImportPage())
this.setState({ type: opt.value })
},
}),
]),
this.renderImportView(),
]),
])
)
}
AccountImportSubview.prototype.componentWillUnmount = function () {
this.props.dispatch(actions.displayWarning(''))
}
AccountImportSubview.prototype.renderImportView = function () {
const props = this.props
const state = this.state || {}
const { type } = state
const { menuItems } = props
const current = type || menuItems[0]
switch (current) {
case 'Private Key':
return h(PrivateKeyImportView)
case 'JSON File':
return h(JsonImportView)
case 'Contract':
return h(ContractImportView)
default:
return h(JsonImportView)
const mapDispatchToProps = dispatch => {
return {
goHome: () => dispatch(actions.goHome()),
displayWarning: warning => dispatch(actions.displayWarning(warning)),
showImportPage: options => dispatch(actions.showImportPage()),
}
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountImportSubview)

View File

@ -1,23 +1,47 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../../../ui/app/actions')
const connect = require('react-redux').connect
const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
const ethNetProps = require('eth-net-props')
const { getCurrentKeyring, ifLooseAcc, ifContractAcc } = require('../util')
const { getHdPaths } = require('./connect-hardware/util')
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import actions from '../../../ui/app/actions'
import { connect } from 'react-redux'
import { Dropdown, DropdownMenuItem } from './dropdown'
import Identicon from './identicon'
import ethUtil from 'ethereumjs-util'
import copyToClipboard from 'copy-to-clipboard'
import ethNetProps from 'eth-net-props'
import { getCurrentKeyring, ifLooseAcc, ifContractAcc, ifHardwareAcc } from '../util'
import { getHdPaths, isLedger } from './connect-hardware/util'
import { LEDGER } from './connect-hardware/enum'
import { importTypes, labels } from '../accounts/import/enums'
import { getFullABI } from '../accounts/import/helpers'
import log from 'loglevel'
import Web3 from 'web3'
class AccountsDropdownMenuItemWrapper extends DropdownMenuItem {
render () {
return (
<DropdownMenuItem
style={{
padding: '8px 0px',
}}
closeMenu={() => {}}
onClick={() => this.props.onClick()}
>
<span className="acc-dd-menu-item-text">{this.props.label}</span>
</DropdownMenuItem>
)
}
}
class AccountDropdowns extends Component {
constructor (props) {
super(props)
const web3 = new Web3(global.ethereumProvider)
this.state = {
accountSelectorActive: false,
optionsMenuActive: false,
web3,
labels: {},
isProxy: false,
contractProps: null,
}
this.accountSelectorToggleClassName = 'accounts-selector'
this.optionsMenuToggleClassName = 'account-dropdown'
@ -25,16 +49,9 @@ class AccountDropdowns extends Component {
renderAccounts () {
const { identities, selected, keyrings, network } = this.props
const accountOrder = keyrings.reduce((list, keyring) => {
if (ifContractAcc(keyring) && keyring.network === network) {
list = list.concat(keyring.accounts)
} else if (!ifContractAcc(keyring)) {
list = list.concat(keyring.accounts)
}
return list
}, [])
const accountOrder = this.getAccounts()
return accountOrder.map((address, index) => {
const accountsViews = accountOrder.map((address, index) => {
const identity = identities[address]
if (!identity) {
return null
@ -54,344 +71,355 @@ class AccountDropdowns extends Component {
return this.accountsDropdownItemView(index, isSelected, keyring, identity)
}
})
return accountsViews
}
accountsDropdownItemView (index, isSelected, keyring, identity) {
return h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
this.props.actions.showAccountDetail(identity.address)
if (this.ifHardwareAcc(keyring)) {
const ledger = 'ledger'
if (keyring.type.toLowerCase().includes(ledger)) {
const hdPaths = getHdPaths()
return new Promise((resolve, reject) => {
this.props.actions.connectHardwareAndUnlockAddress(ledger, hdPaths[1].value, identity.address)
.then(_ => resolve())
.catch(e => {
this.props.actions.connectHardwareAndUnlockAddress(ledger, hdPaths[0].value, identity.address)
.then(_ => resolve())
.catch(e => reject(e))
})
})
.catch(e => {
this.props.actions.displayWarning((e && e.message) || e)
this.props.actions.displayToast(e)
})
}
}
},
style: {
marginTop: index === 0 ? '5px' : '',
fontSize: '16px',
padding: '8px 0px',
},
},
[
isSelected ? h('div', {
style: {
width: '4px',
height: '26px',
background: '#60db97',
position: 'absolute',
left: '-25px',
},
}) : null,
h(
Identicon,
{
overflow: 'none',
address: identity.address,
diameter: 24,
style: {
marginLeft: '10px',
},
},
),
h('span', {
style: {
marginLeft: '10px',
fontSize: '16px',
maxWidth: '95px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
color: isSelected ? 'white' : '',
},
}, identity.name || ''),
this.indicateIfLoose(keyring),
ifLooseAcc(keyring) ? h('.remove', {
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
this.props.actions.showDeleteImportedAccount(identity)
this.setState({
accountSelectorActive: false,
optionsMenuActive: false,
})
},
}) : null,
]
)
const { labels } = this.state
const { address, name } = identity
const leftBorder = isSelected ? <div className="accs-dd-menu-item-selected" /> : null
const accountIcon = (
<Identicon
overflow="none"
address={identity.address}
diameter={24}
style={{ marginLeft: '10px' }}
/>
)
const accountName = (
<span
className="accs-dd-menu-item-account-name"
style={{ color: isSelected ? 'white' : '' }}
>{name || ''}
</span>
)
const accountLabel = labels[address] ? <div className="keyring-label">{labels[address]}</div> : null
const removeIcon = ifLooseAcc(keyring) ? (
<div
className="remove"
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
this.props.actions.showDeleteImportedAccount(identity)
this.setState({
accountSelectorActive: false,
optionsMenuActive: false,
})
}}
/>) : null
return (
<DropdownMenuItem
key={`account_${index}`}
closeMenu={() => {}}
onClick={() => this.accountOnClick(keyring, address)}
style={{
marginTop: index === 0 ? '5px' : '',
fontSize: '16px',
padding: '8px 0px',
}}
>
{leftBorder}
{accountIcon}
{accountName}
{accountLabel}
{removeIcon}
</DropdownMenuItem>
)
}
ifHardwareAcc (keyring) {
if (keyring && keyring.type.search('Hardware') !== -1) {
return true
accountOnClick (keyring, address) {
this.props.actions.showAccountDetail(address)
if (ifHardwareAcc(keyring)) {
if (isLedger(keyring.type)) {
const hdPaths = getHdPaths()
return new Promise((resolve, reject) => {
this.props.actions.connectHardwareAndUnlockAddress(LEDGER, hdPaths[1].value, address)
.then(_ => resolve())
.catch(e => {
this.props.actions.connectHardwareAndUnlockAddress(LEDGER, hdPaths[0].value, address)
.then(_ => resolve())
.catch(e => reject(e))
})
})
.catch(e => {
this.props.actions.displayWarning((e && e.message) || e)
this.props.actions.displayToast(e)
})
}
}
return false
}
indicateIfLoose (keyring) {
ifProxyAcc (address, setProxy) {
return new Promise((resolve, reject) => {
this.props.actions.getContract(address)
.then(contractProps => {
if (setProxy) {
this.setState({contractProps})
}
resolve(contractProps && contractProps.contractType === importTypes.CONTRACT.PROXY)
})
.catch(e => reject(e))
})
}
setLabel (keyring, address) {
if (ifLooseAcc(keyring)) {
let label
if (ifContractAcc(keyring)) {
label = 'CONTRACT'
} else if (this.ifHardwareAcc(keyring)) {
label = 'HARDWARE'
const setProxy = false
this.ifProxyAcc(address, setProxy)
.then(isProxy => {
label = isProxy ? labels.PROXY : labels.CONTRACT
this.setLabelToState(label, address)
})
} else if (ifHardwareAcc(keyring)) {
label = labels.HARDWARE
this.setLabelToState(label, address)
} else {
label = 'IMPORTED'
label = labels.IMPORTED
this.setLabelToState(label, address)
}
return h('.keyring-label', label)
}
}
return null
setLabelToState (label, address) {
const labelsArr = this.state.labels
labelsArr[address] = label
this.setState({labelsArr})
}
renderAccountSelector () {
const { actions } = this.props
const { accountSelectorActive } = this.state
let menuItems = []
menuItems = Object.assign(menuItems, this.renderAccounts())
const bottomMenuItems = [
<AccountsDropdownMenuItemWrapper key="AccountsDropdownMenuItemAdd" onClick={() => actions.addNewAccount()} label="Create Account" />,
<AccountsDropdownMenuItemWrapper key="AccountsDropdownMenuItemImport" onClick={() => actions.showImportPage()} label="Import Account" />,
<AccountsDropdownMenuItemWrapper key="AccountsDropdownMenuItemConnectHD" onClick={() => actions.showConnectHWWalletPage()} label="Connect hardware wallet" />,
]
menuItems = menuItems.concat(bottomMenuItems)
return h(
Dropdown,
{
useCssTransition: true, // Hardcoded because account selector is temporarily in app-header
style: {
return (
<Dropdown
useCssTransition={true} // Hardcoded because account selector is temporarily in app-header
style={{
position: 'absolute',
marginLeft: '-213px',
top: '38px',
minWidth: '180px',
maxHeight: accountSelectorActive ? '300px' : '0px',
width: '265px',
},
innerStyle: {
}}
innerStyle={{
padding: '8px 25px',
},
isOpen: accountSelectorActive,
onClickOutside: (event) => {
}}
isOpen={accountSelectorActive}
onClickOutside={(event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
if (accountSelectorActive && isNotToggleElement) {
this.setState({ accountSelectorActive: false })
}
},
},
[
...this.renderAccounts(),
h(
DropdownMenuItem,
{
style: {
padding: '8px 0px',
},
closeMenu: () => {},
onClick: () => actions.addNewAccount(),
},
[
h('span', { style: { fontSize: '16px', color: '#60db97' } }, 'Create Account'),
],
),
h(
DropdownMenuItem,
{
style: {
padding: '8px 0px',
},
closeMenu: () => {},
onClick: () => actions.showImportPage(),
},
[
h('span', {
style: {
fontSize: '16px',
marginBottom: '5px',
color: '#60db97',
},
}, 'Import Account'),
]
),
h(
DropdownMenuItem,
{
style: {
padding: '8px 0px',
},
closeMenu: () => {},
onClick: () => actions.showConnectHWWalletPage(),
},
[
h('span', {
style: {
fontSize: '16px',
marginBottom: '5px',
color: '#60db97',
},
}, 'Connect hardware wallet'),
]
),
]
}}
>
{menuItems}
</Dropdown>
)
}
renderAccountOptions () {
const { actions, selected, network, keyrings, identities } = this.props
const { optionsMenuActive } = this.state
const { optionsMenuActive, isProxy } = this.state
const keyring = getCurrentKeyring(selected, network, keyrings, identities)
return h(
Dropdown,
{
style: {
return (
<Dropdown
style={{
position: 'relative',
marginLeft: '-234px',
minWidth: '180px',
// marginTop: '30px',
top: '30px',
width: '280px',
},
isOpen: optionsMenuActive,
onClickOutside: (event) => {
}}
isOpen={optionsMenuActive}
onClickOutside={(event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
if (optionsMenuActive && isNotToggleElement) {
this.setState({ optionsMenuActive: false })
}
},
},
[
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, network } = this.props
const url = ethNetProps.explorerLinks.getExplorerAccountLinkFor(selected, network)
global.platform.openWindow({ url })
},
},
`View on block explorer`,
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, identities } = this.props
var identity = identities[selected]
actions.showQrView(selected, identity ? identity.name : '')
},
},
'Show QR Code',
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected } = this.props
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected)
copyToClipboard(checkSumAddress)
},
},
'Copy address to clipboard',
),
ifContractAcc(keyring) ? h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: async () => {
const { selected } = this.props
const contractProps = await this.props.actions.getContract(selected)
const abi = contractProps && contractProps.abi
copyToClipboard(JSON.stringify(abi))
},
},
'Copy ABI to clipboard',
) : null,
(!this.ifHardwareAcc(keyring) && !(ifContractAcc(keyring))) ? h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
actions.requestAccountExport()
},
},
'Export Private Key',
) : null,
]
}}
>
<DropdownMenuItem
closeMenu={() => {}}
onClick={() => this.viewOnBlockExplorer()}
>View on block explorer</DropdownMenuItem>
<DropdownMenuItem
closeMenu={() => {}}
onClick={() => this.showQRCode()}
>Show QR Code</DropdownMenuItem>
<DropdownMenuItem
closeMenu={() => {}}
onClick={() => this.copyAddress()}
>Copy address to clipboard</DropdownMenuItem>
{ifContractAcc(keyring) ? <DropdownMenuItem
closeMenu={() => {}}
onClick={() => this.copyABI()}
>Copy ABI to clipboard</DropdownMenuItem> : null}
{isProxy ? <DropdownMenuItem
closeMenu={() => {}}
onClick={() => this.updateABI()}
>Update implementation ABI</DropdownMenuItem> : null}
{(!ifHardwareAcc(keyring) && !(ifContractAcc(keyring))) ? <DropdownMenuItem
closeMenu={() => {}}
onClick={() => actions.requestAccountExport()}
>Export Private Key</DropdownMenuItem> : null}
</Dropdown>
)
}
viewOnBlockExplorer = () => {
const { selected, network } = this.props
const url = ethNetProps.explorerLinks.getExplorerAccountLinkFor(selected, network)
global.platform.openWindow({ url })
}
showQRCode = () => {
const { selected, identities, actions } = this.props
const identity = identities[selected]
actions.showQrView(selected, identity ? identity.name : '')
}
copyAddress = () => {
const { selected } = this.props
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected)
copyToClipboard(checkSumAddress)
}
copyABI = async () => {
const { contractProps } = this.state
const abi = contractProps && contractProps.abi
copyToClipboard(JSON.stringify(abi))
}
updateABI = async () => {
const { actions, selected, network } = this.props
const { web3 } = this.state
actions.showLoadingIndication()
getFullABI(web3.eth, selected, network, importTypes.CONTRACT.PROXY)
.then(finalABI => {
actions.updateABI(selected, network, finalABI)
.then()
.catch(e => {
log.debug(e)
})
.finally(() => actions.hideLoadingIndication())
})
.catch(e => {
log.debug(e)
actions.hideLoadingIndication()
})
}
render () {
const { style, enableAccountsSelector, enableAccountOptions } = this.props
const { optionsMenuActive, accountSelectorActive } = this.state
return h(
'span',
{
style: style,
},
[
enableAccountsSelector && h(
'div.accounts-selector',
{
style: {
background: 'url(images/switch_acc.svg) white center center no-repeat',
height: '25px',
width: '25px',
marginRight: '3px',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: !accountSelectorActive,
optionsMenuActive: false,
})
},
},
this.renderAccountSelector(),
),
enableAccountOptions && h(
'div.address-dropdown.account-dropdown',
{
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: false,
optionsMenuActive: !optionsMenuActive,
})
},
},
this.renderAccountOptions()
),
]
const accountSelector = enableAccountsSelector && (
<div
className="accounts-selector accounts-selector-additional-style"
onClick={(event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: !accountSelectorActive,
optionsMenuActive: false,
})
}}
>
{this.renderAccountSelector()}
</div>
)
const accountOptions = enableAccountOptions && (
<div
className="address-dropdown account-dropdown"
onClick={(event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: false,
optionsMenuActive: !optionsMenuActive,
})
}}
>
{this.renderAccountOptions()}
</div>
)
return (
<span style={style}>
{accountSelector}
{accountOptions}
</span>
)
}
getAccounts () {
const { keyrings, network } = this.props
const accountOrder = keyrings.reduce((list, keyring) => {
if (ifContractAcc(keyring) && keyring.network === network) {
list = list.concat(keyring.accounts)
} else if (!ifContractAcc(keyring)) {
list = list.concat(keyring.accounts)
}
return list
}, [])
return accountOrder
}
checkIfProxy () {
const { selected } = this.props
const setProxy = true
this.ifProxyAcc(selected, setProxy)
.then(isProxy => {
this.setState({isProxy})
})
}
setAllLabels () {
const { identities, keyrings, network } = this.props
const accountOrder = this.getAccounts()
accountOrder.forEach((address) => {
const keyring = getCurrentKeyring(address, network, keyrings, identities)
this.setLabel(keyring, address)
})
}
componentDidMount () {
this.setAllLabels()
this.checkIfProxy()
}
// switch to the first account in the list on network switch, if unlocked account was contract before change
componentDidUpdate (prevProps) {
const { selected, keyrings } = this.props
if (prevProps.selected !== selected) {
this.checkIfProxy()
}
if (prevProps.keyrings.length !== keyrings.length) {
this.setAllLabels()
}
if (!isNaN(this.props.network)) {
const { selected, network, keyrings, identities } = this.props
const { network } = this.props
if (network !== prevProps.network) {
const { keyrings, identities } = this.props
const keyring = getCurrentKeyring(selected, this.props.network, keyrings, identities)
const firstKeyring = keyrings && keyrings[0]
const firstKeyRingAcc = firstKeyring && firstKeyring.accounts && firstKeyring.accounts[0]
if (!keyring || (ifContractAcc(keyring) && firstKeyRingAcc)) {
return this.props.actions.showAccountDetail(firstKeyRingAcc)
}
this.setAllLabels()
}
}
}
@ -416,6 +444,8 @@ AccountDropdowns.propTypes = {
const mapDispatchToProps = (dispatch) => {
return {
actions: {
showLoadingIndication: () => dispatch(actions.showLoadingIndication()),
hideLoadingIndication: () => dispatch(actions.hideLoadingIndication()),
showConfigPage: () => dispatch(actions.showConfigPage()),
requestAccountExport: () => dispatch(actions.requestExportAccount()),
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
@ -434,6 +464,7 @@ const mapDispatchToProps = (dispatch) => {
},
displayToast: (msg) => dispatch(actions.displayToast(msg)),
hideToast: () => dispatch(actions.hideToast()),
updateABI: (address, network, abi) => dispatch(actions.updateABI(address, network, abi)),
},
}
}

View File

@ -450,7 +450,11 @@ class AddTokenScreen extends Component {
const { symbol = '', decimals = '' } = await this.tokenInfoGetter(address)
const autoFilled = Boolean(symbol && decimals)
this.setState({ autoFilled })
this.setState({
autoFilled,
warning: '',
customAddressError: null,
})
this.handleCustomSymbolChange(symbol || '')
this.handleCustomDecimalsChange(decimals || '')
}

View File

@ -1,4 +1,5 @@
import { LEDGER, TREZOR } from './enum'
function isLedger (device) {
return device && device.toLowerCase().includes(LEDGER)
}

View File

@ -26,15 +26,18 @@ CopyButton.prototype.render = function () {
const message = copied ? 'Copied' : props.title || ' Copy '
const defaultCopyStyles = ['clipboard', 'cursor-pointer']
const originalStyle = {
display: display || 'flex',
alignItems: 'center',
}
const style = Object.assign(originalStyle, this.props.style)
return h('.copy-button', {
style: {
display: display || 'flex',
alignItems: 'center',
},
style,
}, [
h(Tooltip, {
title: message,
position: this.props.tooltipPosition,
}, [
h('i', {
style: {

View File

@ -94,7 +94,7 @@ class DropdownMenuItem extends Component {
}
DropdownMenuItem.propTypes = {
closeMenu: PropTypes.func.isRequired,
closeMenu: PropTypes.func,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
style: PropTypes.object,

View File

@ -11,6 +11,7 @@ import actions from '../../../../ui/app/actions'
import abiEncoder from 'web3-eth-abi'
import Web3 from 'web3'
import copyToClipboard from 'copy-to-clipboard'
import CopyButton from '../copyButton'
class SendTransactionField extends Component {
constructor (props) {
@ -221,10 +222,12 @@ class SendTransactionScreen extends PersistentForm {
style={{ marginTop: '10px' }}
>
{params.name || `${paramName} ${ind + 1}`}
{!isInput ? <i
className="clipboard cursor-pointer"
style={{ marginLeft: '10px' }}
onClick={(e) => { copyToClipboard(defaultValue) }}
{!isInput ? <CopyButton
value={defaultValue}
style={{
display: 'inline-block',
marginLeft: '5px',
}}
/> : null}
</h3>
)

View File

@ -134,38 +134,47 @@ TokenList.prototype.renderTokenStatusBar = function () {
const tokensFromCurrentNetwork = tokens.filter(token => (parseInt(token.network) === parseInt(network) || !token.network))
let msg
let noTokens = false
if (tokensFromCurrentNetwork.length === 1) {
msg = `You own 1 token`
} else if (tokensFromCurrentNetwork.length > 1) {
msg = `You own ${tokensFromCurrentNetwork.length} tokens`
} else {
msg = `No tokens found`
noTokens = true
}
return h('div', {
style: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
minHeight: '70px',
padding: '30px 30px 10px',
},
}, [
h('span', msg),
h('button.btn-primary.wallet-view__add-token-button', {
key: 'reveal-account-bar',
onClick: (event) => {
event.preventDefault()
this.props.addToken()
},
return h('div', [
h('div', {
style: {
display: 'flex',
justifyContent: 'center',
justifyContent: 'space-between',
alignItems: 'center',
minHeight: '70px',
padding: '30px 30px 10px',
},
}, [
'Add Token',
h('span', msg),
h('button.btn-primary.wallet-view__add-token-button', {
key: 'reveal-account-bar',
onClick: (event) => {
event.preventDefault()
this.props.addToken()
},
style: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
}, [
'Add Token',
]),
]),
noTokens ? h('div', {
style: {
height: '70px',
},
}) : null,
])
}

View File

@ -76,7 +76,7 @@ TransactionList.prototype.render = function () {
}, [
h('p', {
style: {
marginTop: '50px',
margin: '50px 0',
},
}, 'No transaction history.'),
]),

View File

@ -0,0 +1,27 @@
.accs-dd-menu-item-selected {
width: 4px;
height: 26px;
background: #60db97;
position: absolute;
left: -25px;
}
.accs-dd-menu-item-account-name {
margin-left: 10px;
font-size: 16px;
max-width: 95px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.acc-dd-menu-item-text {
font-size: 16px;
margin-bottom: 5px;
color: #60db97;
}
.accounts-selector-additional-style {
background: url(images/switch_acc.svg) white center center no-repeat;
height: 25px;
width: 25px;
margin-right: 3px;
}

View File

@ -44,6 +44,7 @@ module.exports = {
getCurrentKeyring,
ifLooseAcc,
ifContractAcc,
ifHardwareAcc,
}
function valuesFor (obj) {
@ -323,6 +324,7 @@ function ifLooseAcc (keyring) {
} catch (e) { return }
}
/**
* checks, if keyring is contract
*
@ -337,3 +339,17 @@ function ifContractAcc (keyring) {
return isContract
} catch (e) { return }
}
/**
* checks, if keyring is of hardware type
*
* @param {object} keyring
*
* returns {boolean} true, if keyring is of hardware type and false, if it is not
**/
function ifHardwareAcc (keyring) {
if (keyring && keyring.type.search('Hardware') !== -1) {
return true
}
return false
}

View File

@ -17,6 +17,7 @@ var cssFiles = {
'first-time.css': fs.readFileSync(path.join(__dirname, '../mascara/src/app/first-time/index.css'), 'utf8'),
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
'dropdowns.css': fs.readFileSync(path.join(__dirname, '/app/css/dropdowns.css'), 'utf8'),
}
function bundleCss () {

8
package-lock.json generated
View File

@ -16138,7 +16138,7 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
@ -16160,7 +16160,7 @@
"dev": true
},
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"requires": {
"bn.js": "^4.10.0",
@ -19581,7 +19581,7 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
@ -19597,7 +19597,7 @@
}
},
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"requires": {
"bn.js": "^4.10.0",

View File

@ -20,8 +20,8 @@
"test:e2e:chrome:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-all.sh",
"test:e2e:firefox": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:firefox'",
"test:e2e:firefox:beta": "SELENIUM_BROWSER=firefox test/e2e/beta/run-all.sh",
"test:e2e:run:chrome": "SELENIUM_BROWSER=chrome mocha test/e2e/metamask.spec --bail --recursive",
"test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/metamask.spec --bail --recursive",
"test:e2e:run:chrome": "SELENIUM_BROWSER=chrome mocha test/e2e/nw.spec --bail --recursive",
"test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/nw.spec --bail --recursive",
"test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'",
"test:screens:run": "node test/screens/new-ui.js",
"test:coverage": "nyc --reporter=text --reporter=html npm run test:unit && npm run test:coveralls-upload",

View File

@ -252,7 +252,8 @@ module.exports = {
error: By.css('span.error'),
selectArrow: By.className('Select-arrow-zone'),
selectType: By.name('import-type-select'),
itemContract: By.id('react-select-3--option-2'),
itemContract: By.id('react-select-4--option-2'),
itemProxyContract: By.id('react-select-3--option-3'),
contractAddress: By.id('address-box'),
contractABI: By.id('abi-box'),
title: By.css('#app-content > div > div.app-primary.from-right > div > div:nth-child(2) > div.flex-row.flex-center > h2'),
@ -309,13 +310,14 @@ module.exports = {
menu: By.id('wallet-view__tab-tokens'),
token: By.className('token-cell'),
balance: By.css('#token-cell_0 > h3'),
amount: By.css('#app-content > div > div.app-primary.from-left > div > section > div.full-flex-height > div > span'),
amount: By.css('#app-content > div > div.app-primary.from-left > div > section > div.full-flex-height > div > div:nth-child(1) > span'),
textNoTokens: 'No tokens found',
textYouOwn1token: 'You own 1 token',
buttonAdd: By.css('div.full-flex-height:nth-child(2) > div:nth-child(1) > button:nth-child(2)'),
buttonAdd: By.css('#app-content > div > div.app-primary.from-left > div > section > div.full-flex-height > div > div:nth-child(1) > button'),
buttonAdd2: By.css('#app-content > div > div.app-primary.from-right > div > section > div.full-flex-height > div > div:nth-child(1) > button'),
buttonAddText: 'Add Token',
counter: By.css('#app-content > div > div.app-primary.from-left > div > section > div.full-flex-height > div > span'),
counterFF: By.css('div.full-flex-height:nth-child(2) > div:nth-child(1) > span:nth-child(1)'),
counter: By.css('#app-content > div > div.app-primary.from-left > div > section > div.full-flex-height > div > div:nth-child(1) > span'),
counterFF: By.css('div.full-flex-height:nth-child(2) > div:nth-child(1) > div:nth-child(1) > span:nth-child(1)'),
},
},
buyEther: {

View File

@ -233,7 +233,8 @@ class Functions {
try {
const tab = await this.waitUntilShowUp(screens.main.tokens.menu)
await this.click(tab)
const button = await this.waitUntilShowUp(screens.main.tokens.buttonAdd, 300)
await this.delay(1000)
const button = await this.waitUntilShowUp(screens.main.tokens.buttonAdd2, 300)
await this.click(button)
let count = 20
do {

File diff suppressed because it is too large Load Diff

139
test/e2e/nw.spec.js Normal file
View File

@ -0,0 +1,139 @@
const path = require('path')
const Func = require('./func').Functions
const account1 = '0x2E428ABd9313D256d64D1f69fe3929C3BE18fD1f'
const account2 = '0xd7b7AFeCa35e32594e29504771aC847E2a803742'
const testsFolder = './test-cases'
const setup = require(`${testsFolder}/setup.spec`)
const login = require(`${testsFolder}/login.spec`)
const { accountCreation, getCreatedAccounts } = require(`${testsFolder}/account-creation.spec`)
const connectHDWallet = require(`${testsFolder}/connect-hd-wallet.spec`)
const importAccount = require(`${testsFolder}/import-account.spec`)
const importContractAccount = require(`${testsFolder}/import-contract-account.spec`)
const deleteImportedAccount = require(`${testsFolder}/delete-imported-account.spec`)
const signData = require(`${testsFolder}/sign-data.spec`)
const exportPrivateKey = require(`${testsFolder}/export-private-key.spec`)
const importGanacheSeedPhrase = require(`${testsFolder}/import-ganache-seed-phrase.spec`)
const checkEmittedEvents = require(`${testsFolder}/check-emitted-events.spec`)
const addCustomToken = require(`${testsFolder}/add-token-custom.spec`)
const changePassword = require(`${testsFolder}/change-password.spec`)
const addTokeFromSearch = require(`${testsFolder}/add-token-search.spec`)
const customRPC = require(`${testsFolder}/custom-rpc.spec`)
describe('Metamask popup page', async function () {
this.timeout(15 * 60 * 1000)
const f = new Func()
let driver, extensionId
const password = '123456789'
const newPassword = {
correct: 'abcDEF123!@#',
short: '123',
incorrect: '1234567890',
}
before(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const extPath = path.resolve('dist/chrome')
driver = await Func.buildChromeWebDriver(extPath)
f.driver = driver
extensionId = await f.getExtensionIdChrome()
f.extensionId = extensionId
await driver.get(`chrome-extension://${extensionId}/popup.html`)
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
const extPath = path.resolve('dist/firefox')
driver = await Func.buildFirefoxWebdriver()
f.driver = driver
await f.installWebExt(extPath)
await f.delay(700)
extensionId = await f.getExtensionIdFirefox()
f.extensionId = extensionId
await driver.get(`moz-extension://${extensionId}/popup.html`)
}
})
afterEach(async function () {
// logs command not supported in firefox
// https://github.com/SeleniumHQ/selenium/issues/2910
if (process.env.SELENIUM_BROWSER === 'chrome') {
// check for console errors
const errors = await f.checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
console.log(errorMessage)
}
}
// gather extra data if test failed
if (this.currentTest.state === 'failed') {
await f.verboseReportOnFailure(this.currentTest)
}
})
after(async function () {
await driver.quit()
})
describe('Setup', async () => {
await setup(f)
})
describe('Log In', async () => {
await login(f, password)
})
describe('Account Creation', async () => {
await accountCreation(f, password)
})
describe('Connect Hardware Wallet', async () => {
await connectHDWallet(f)
})
describe('Import Account', async () => {
await importAccount(f)
})
describe('Import Contract account', async () => {
await importContractAccount(f, account1, getCreatedAccounts)
})
describe('Delete Imported Account', async () => {
await deleteImportedAccount(f)
})
describe('Sign Data', async () => {
await signData(f)
})
describe('Export private key', async () => {
await exportPrivateKey(f, password)
})
describe('Import Ganache seed phrase', async () => {
await importGanacheSeedPhrase(f, account2, password)
})
describe('Check the filter of emitted events', async () => {
await checkEmittedEvents(f, account1, account2)
})
describe('Add Token: Custom', async () => {
await addCustomToken(f, account1, account2)
})
describe('Change password', async () => {
await changePassword(f, password, newPassword)
})
describe('Add Token:Search', async () => {
await addTokeFromSearch(f)
})
describe('Custom Rpc', async () => {
await customRPC(f)
})
})

View File

@ -0,0 +1,150 @@
const assert = require('assert')
const { menus, screens, NETWORKS } = require('../elements')
const clipboardy = require('clipboardy')
const createdAccounts = []
const accountCreation = async (f, password) => {
const newAccountName = 'new name'
it('sets provider type to localhost', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
await f.delay(2000)
})
it('copy icon is displayed and clickable', async () => {
const field = await f.waitUntilShowUp(screens.main.iconCopy)
await field.click()
assert.notEqual(field, false, 'copy icon doesn\'t present')
})
it("Account's address is displayed and has length 20 symbols", async () => {
const field = await f.waitUntilShowUp(screens.main.address)
createdAccounts.push((await field.getText()).toUpperCase())
console.log(createdAccounts[0])
assert.notEqual(createdAccounts[0].length, 20, "address isn't displayed")
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.length, 42, "address account wasn't copied to clipboard")
})
it('open \'Account name\' change dialog', async () => {
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
const field = await f.waitUntilShowUp(screens.main.edit)
await field.click()
const accountName = await f.waitUntilShowUp(screens.main.fieldAccountName)
assert.notEqual(accountName, false, '\'Account name\' change dialog isn\'t opened')
assert.equal(await accountName.getAttribute('value'), 'Account 1', 'incorrect account name')
})
it('fill out new account\'s name', async () => {
const field = await f.waitUntilShowUp(screens.main.fieldAccountName)
await field.clear()
await field.sendKeys(newAccountName)
})
it('dialog \'Account name\' is disappeared if click button \'Save\'', async () => {
const button = await f.waitUntilShowUp(screens.main.buttons.save)
assert.equal(await button.getText(), 'Save', 'button has incorrect name')
assert.notEqual(button, true, 'button \'Save\' does not present')
await f.click(button)
const accountName = await f.waitUntilShowUp(screens.main.fieldAccountName, 10)
assert.equal(accountName, false, '\'Account name\' change dialog isn\'t opened')
})
it('account has new name', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const account1 = await f.waitUntilShowUp(menus.account.account1)
assert.equal(await account1.getText(), newAccountName, 'account\'s name didn\'t changed')
await accountMenu.click()
})
it('adds a second account', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.createAccount)
await item.click()
})
it("Account's address is displayed and has length 20 symbols", async () => {
const field = await f.waitUntilShowUp(screens.main.address)
createdAccounts.push((await field.getText()).toUpperCase())
console.log(createdAccounts[1])
assert.notEqual(createdAccounts[1].length, 20, "address isn't displayed")
})
it('logs out of the vault', async () => {
const menu = await f.waitUntilShowUp(menus.sandwich.menu)
await menu.click()
await f.delay(500)
const button = await f.waitUntilShowUp(menus.sandwich.logOut)
assert.equal(await button.getText(), 'Log Out', 'button has incorrect name')
await button.click()
})
it('accepts account password after lock', async () => {
const box = await f.waitUntilShowUp(screens.lock.fieldPassword)
await box.sendKeys(password)
const button = await f.waitUntilShowUp(screens.lock.buttonLogin)
assert.equal(await button.getText(), 'Log In', 'button has incorrect name')
await f.click(button)
})
it('shows QR code option', async () => {
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.dot.showQRcode)
await item.click()
})
it('checks QR code address is the same as account details address', async () => {
const field = await f.waitUntilShowUp(screens.QRcode.address)
const text = await field.getText()
assert.equal(text.toUpperCase(), createdAccounts[1], 'QR address doesn\'t match')
})
it('copy icon is displayed and clickable', async () => {
const field = await f.waitUntilShowUp(screens.QRcode.iconCopy)
await field.click()
assert.notEqual(field, false, 'copy icon doesn\'t present')
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.length, 42, "address account wasn't copied to clipboard")
})
it('close QR code screen by clicking button arrow', async () => {
const button = await f.waitUntilShowUp(screens.QRcode.buttonArrow)
await f.click(button)
})
it('user is able to open \'Info\' screen', async () => {
const accountMenu = await f.waitUntilShowUp(menus.sandwich.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.sandwich.info)
await item.click()
})
it('screen \'Info\' has correct title', async () => {
const title = await f.waitUntilShowUp(screens.info.title)
assert.equal(await title.getText(), screens.info.titleText, 'title is incorrect')
})
it('close \'Info\' screen by clicking button arrow', async () => {
const button = await f.waitUntilShowUp(screens.info.buttonArrow)
await button.click()
})
}
const getCreatedAccounts = () => {
return createdAccounts
}
module.exports = {
accountCreation,
getCreatedAccounts,
}

View File

@ -0,0 +1,504 @@
const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements')
const token = { supply: 101, name: 'Test', decimals: 0, ticker: 'ABC' }
let tokenAddress
const addCustomToken = async (f, account1, account2) => {
describe('Add token to LOCALHOST', function () {
it('Create custom token in LOCALHOST', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
tokenAddress = await f.createToken(account1, token, true)
console.log('Token contract address: ' + tokenAddress)
assert.equal(tokenAddress.length, 42, 'failed to create token')
})
it('navigates to the add token screen', async () => {
await f.waitUntilShowUp(screens.main.identicon)
const tab = await f.waitUntilShowUp(screens.main.tokens.menu)
await tab.click()
const addTokenButton = await f.waitUntilShowUp(screens.main.tokens.buttonAdd2)
assert.equal(await addTokenButton.getText(), screens.main.tokens.buttonAddText)
await f.click(addTokenButton)
await f.delay(2000)
})
it('checks add token screen has correct title', async () => {
const addTokenScreen = await f.waitUntilShowUp(screens.addToken.title)
assert.equal(await addTokenScreen.getText(), screens.addToken.titleText)
})
it('adds token parameters', async () => {
const tab = await f.waitUntilShowUp(screens.addToken.tab.custom, 30)
if (!await f.waitUntilShowUp(screens.addToken.custom.fields.contractAddress)) await tab.click()
})
it('address input is displayed and has correct placeholder', async () => {
const field = await f.waitUntilShowUp(screens.addToken.custom.fields.contractAddress)
assert.equal(await field.getAttribute('placeholder'), 'Token Contract Address', 'incorrect placeholder')
})
it('fill out address input', async () => {
const tokenContractAddress = await f.waitUntilShowUp(screens.addToken.custom.fields.contractAddress)
await tokenContractAddress.sendKeys(tokenAddress)
await f.delay(2000)
})
it('field \'Symbol\' enabled and has correct value', async () => {
const field = await f.waitUntilShowUp(screens.addToken.custom.fields.tokenSymbol)
assert.equal(await field.isEnabled(), true, 'field disabled')
assert.equal(await field.getAttribute('placeholder'), 'Like "ETH"', 'incorrect placeholder')
assert.equal(await field.getAttribute('value'), token.ticker, 'incorrect value')
})
it('field \'Decimals\' enabled and has correct value', async () => {
const field = await f.waitUntilShowUp(screens.addToken.custom.fields.decimals)
assert.equal(await field.isEnabled(), false, 'field disabled')
assert.equal(await field.getAttribute('value'), token.decimals, 'incorrect value')
})
it('checks the token balance', async () => {
const button = await f.waitUntilShowUp(screens.addToken.custom.buttons.add)
await f.click(button)
await f.delay(2000)
const tokenBalance = await f.waitUntilShowUp(screens.main.tokens.balance)
assert.equal(await tokenBalance.getText(), token.supply + ' ' + token.ticker, 'balance is incorrect or not displayed')
})
it('click to token opens the etherscan', async () => {
const link = await f.waitUntilShowUp(screens.main.tokens.token)
await link.click()
await f.delay(2000)
const allHandles = await f.driver.getAllWindowHandles()
console.log('allHandles.length ' + allHandles.length)
assert.equal(allHandles.length, 2, 'etherscan wasn\'t opened')
await f.switchToLastPage()
await f.delay(2000)
const title = await f.waitUntilCurrentUrl()
console.log(title)
assert.equal(title.includes('https://etherscan.io/token/'), true, 'etherscan wasn\'t opened')
await f.switchToFirstPage()
})
})
describe('Token menu', function () {
it('token menu is displayed and clickable ', async () => {
const menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
})
it('link \'View on blockexplorer...\' leads to correct page ', async () => {
const menu = await f.waitUntilShowUp(menus.token.view)
assert.notEqual(menu, false, 'item isn\'t displayed')
assert.equal(await menu.getText(), menus.token.viewText, 'incorrect name')
await menu.click()
await f.delay(2000)
const allHandles = await f.driver.getAllWindowHandles()
console.log('allHandles.length ' + allHandles.length)
assert.equal(allHandles.length, 3, 'etherscan wasn\'t opened')
await f.switchToLastPage()
const title = await f.waitUntilCurrentUrl()
console.log(title)
assert.equal(title.includes('https://etherscan.io/token/'), true, 'etherscan wasn\'t opened')
await f.switchToFirstPage()
})
it('item \'Copy\' is displayed and clickable ', async () => {
let menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.token.copy)
assert.notEqual(item, false, 'item isn\'t displayed')
assert.equal(await item.getText(), menus.token.copyText, 'incorrect name')
await item.click()
menu = await f.waitUntilShowUp(menus.token.menu, 10)
assert.notEqual(menu, false, 'menu wasn\'t closed')
})
it('item \'Remove\' is displayed', async () => {
const menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.token.remove)
assert.notEqual(item, false, 'item isn\'t displayed')
assert.equal(await item.getText(), menus.token.removeText, 'incorrect name')
})
it('item \'Send \' is displayed', async () => {
const item = await f.waitUntilShowUp(menus.token.send)
assert.notEqual(item, false, 'item isn\'t displayed')
assert.equal(await item.getText(), menus.token.sendText, 'incorrect name')
await f.waitUntilShowUp(menus.token.menu)
})
})
describe('Check support of token per network basis ', async () => {
const inexistentToken = '0xB8c77482e45F1F44dE1745F52C74426C631bDD51'
describe('Token should be displayed only for network, where it was added ', async () => {
it('token should not be displayed in POA network', async () => {
await f.setProvider(NETWORKS.POA)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in SOKOL network', async () => {
await f.setProvider(NETWORKS.SOKOL)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in MAINNET', async () => {
await f.setProvider(NETWORKS.MAINNET)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in ROPSTEN network', async () => {
await f.setProvider(NETWORKS.ROPSTEN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in KOVAN network', async () => {
await f.setProvider(NETWORKS.KOVAN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in RINKEBY network', async () => {
await f.setProvider(NETWORKS.RINKEBY)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
})
describe.skip('Custom tokens validation ', async () => {
it('can not add inexistent token to POA network', async () => {
await f.setProvider(NETWORKS.POA)
console.log(tokenAddress)
assert(await f.isDisabledAddInexistentToken(tokenAddress), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to SOKOL network', async () => {
await f.setProvider(NETWORKS.SOKOL)
assert(await f.isDisabledAddInexistentToken(inexistentToken), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to ROPSTEN network', async () => {
await f.setProvider(NETWORKS.ROPSTEN)
assert(await f.isDisabledAddInexistentToken(tokenAddress), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to KOVAN network', async () => {
await f.setProvider(NETWORKS.KOVAN)
assert(await f.isDisabledAddInexistentToken(tokenAddress), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to RINKEBY network', async () => {
await f.setProvider(NETWORKS.RINKEBY)
assert(await f.isDisabledAddInexistentToken(tokenAddress), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to MAINNET', async () => {
await f.setProvider(NETWORKS.MAINNET)
assert(await f.isDisabledAddInexistentToken(tokenAddress), true, 'can add inexistent token in POA network')
})
it('can not add inexistent token to LOCALHOST network', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
assert(await f.isDisabledAddInexistentToken(tokenAddress.slice(0, tokenAddress.length - 2) + '0'), true, 'can add inexistent token in POA network')
})
it('token still should be displayed in LOCALHOST network', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
await f.waitUntilDisappear(screens.main.tokens.amount)
assert.notEqual(await f.waitUntilShowUp(screens.main.tokens.amount), false, 'App is frozen')
const tokens = await f.driver.findElements(screens.main.tokens.amount)
assert.equal(tokens.length, 1, '\'Tokens\' section doesn\'t contain field with amount of tokens')
assert.equal(await tokens[0].getText(), screens.main.tokens.textYouOwn1token, 'Token isn\'t displayed')
})
})
})
describe('Transfer tokens', function () {
const invalidAddress = '0xkqjefwblknnecwe'
const invalidAmount = 'eeeee'
const largeAmount = '123'
const preciseAmount = '0.123456789123456789123'
const negativeAmount = '-1'
it('switch to account 1 ', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account1)
await item.click()
await f.delay(2000)
const accountName = await f.waitUntilShowUp(screens.main.accountName)
assert.equal(await accountName.getText(), 'Account 1', 'account name incorrect')
})
it('open screen \'Transfer tokens\' ', async () => {
const menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.token.send)
await item.click()
})
it('field \'Amount\' is displayed and has correct placeholder ', async () => {
const item = await f.waitUntilShowUp(screens.sendTokens.field.amount)
assert.equal(await item.getAttribute('placeholder'), screens.sendTokens.field.amountPlaceholder, 'placeholder is incorrect')
})
it('field \'Address\' is displayed and has correct placeholder ', async () => {
const item = await f.waitUntilShowUp(screens.sendTokens.field.address)
assert.equal(await item.getAttribute('placeholder'), screens.sendTokens.field.addressPlaceholder, 'placeholder is incorrect')
})
it('token\'s balance is correct ', async () => {
const item = await f.waitUntilShowUp(screens.sendTokens.balance)
assert.equal(await item.getText(), token.supply, 'token\'s balance is incorrect')
})
it('token\'s symbol is correct ', async () => {
const item = await f.waitUntilShowUp(screens.sendTokens.symbol)
assert.equal(await item.getText(), token.ticker, 'token\'s symbol is incorrect')
})
it('error message if invalid token\'s amount', async () => {
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
assert.equal(await button.getText(), 'Next', 'button \'Next\' has incorrect name')
await f.click(button)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.invalidAmount, ' error message is incorrect')
})
it('error message if invalid address', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await amount.sendKeys('1')
const address = await f.waitUntilShowUp(screens.sendTokens.field.address)
await address.sendKeys(invalidAddress)
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
await f.click(button)
await f.delay(2000)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.address, ' error message is incorrect')
})
it('error message if amount is large', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await amount.sendKeys(largeAmount)
const address = await f.waitUntilShowUp(screens.sendTokens.field.address)
await f.clearField(address)
await address.sendKeys(account2)
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
await f.click(button)
await f.delay(2000)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.largeAmount, ' error message is incorrect')
})
it('error message if amount is invalid', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await f.clearField(amount)
await amount.sendKeys(invalidAmount)
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
await f.click(button)
await f.delay(2000)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.invalidAmount, ' error message is incorrect')
})
it.skip('error message if amount is too precise', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await f.clearField(amount)
await amount.sendKeys(preciseAmount)
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
await f.click(button)
await f.delay(2000)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.tooPrecise, ' error message is incorrect')
})
it('error message if amount is negative', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await f.clearField(amount)
await amount.sendKeys(negativeAmount)
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
await f.click(button)
await f.delay(2000)
const error = await f.waitUntilShowUp(screens.sendTokens.error)
assert.equal(await error.getText(), screens.sendTokens.errorText.negativeAmount, ' error message is incorrect')
})
it('\'Confirm transaction\' screen is opened if address and amount are correct', async () => {
const amount = await f.waitUntilShowUp(screens.sendTokens.field.amount)
await f.clearField(amount)
await amount.sendKeys('5')
const button = await f.waitUntilShowUp(screens.sendTokens.button.next)
await f.click(button)
const buttonSubmit = await f.waitUntilShowUp(screens.confirmTransaction.button.submit)
assert.notEqual(buttonSubmit, false, 'incorrect screen was opened')
})
it('\'Confirm transaction\' screen: token\'s amount is correct', async () => {
const amount = await f.waitUntilShowUp(screens.confirmTransaction.amount)
assert.equal(await amount.getText(), '5.000', ' amount is incorrect')
})
it('\'Confirm transaction\' screen: token\'s symbol is correct', async () => {
const symbol = await f.waitUntilShowUp(screens.confirmTransaction.symbol)
assert.equal(await symbol.getText(), token.ticker, ' symbol is incorrect')
})
it('submit transaction', async () => {
await f.driver.navigate().refresh()
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.submit)
await f.click(button)
const list = await f.waitUntilShowUp(screens.main.transactionList)
assert.notEqual(list, false, ' main screen isn\'t opened')
})
it('correct amount substracted from sender\'s tokens balance', async () => {
const tab = await f.waitUntilShowUp(screens.main.tokens.menu)
await tab.click()
await f.driver.navigate().refresh()
await f.delay(5000)
await f.driver.navigate().refresh()
await f.delay(5000)
await f.driver.navigate().refresh()
await f.delay(5000)
const balance = await f.waitUntilShowUp(screens.main.tokens.balance)
assert.equal(await balance.getText(), (token.supply - 5) + ' ' + token.ticker, 'balance is incorrect')
})
it('switch to account 2 ', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account2)
await item.click()
await f.delay(2000)
const accountName = await f.waitUntilShowUp(screens.main.accountName)
assert.equal(await accountName.getText(), 'Account 2', 'account name incorrect')
})
it('added token isn\'t displayed for another account in the same network', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.createAccount)
await item.click()
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
await f.delay(2000)
})
it('add token to another account in the same network', async () => {
const addTokenButton = await f.waitUntilShowUp(screens.main.tokens.buttonAdd)
assert.equal(await addTokenButton.getText(), screens.main.tokens.buttonAddText)
await f.click(addTokenButton)
const tokenContractAddress = await f.waitUntilShowUp(screens.addToken.custom.fields.contractAddress)
await tokenContractAddress.sendKeys(tokenAddress)
const buttonAdd = await f.waitUntilShowUp(screens.addToken.custom.buttons.add)
await f.click(buttonAdd)
})
it('tokens were transfered, balance is updated', async () => {
const balance = await f.waitUntilShowUp(screens.main.tokens.balance)
assert.equal(await balance.getText(), '5 ' + token.ticker, 'balance is incorrect')
})
})
describe('Remove token , provider is localhost', function () {
it('switch to account 1 ', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account1)
await item.click()
await f.delay(2000)
const accountName = await f.waitUntilShowUp(screens.main.accountName)
assert.equal(await accountName.getText(), 'Account 1', 'account name incorrect')
})
it('remove option opens \'Remove token\' screen ', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
const menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
const remove = await f.waitUntilShowUp(menus.token.remove)
await remove.click()
})
it('screen \'Remove token\' has correct title', async () => {
const title = await f.waitUntilShowUp(screens.removeToken.title)
assert.equal(await title.getText(), screens.removeToken.titleText, 'title is incorrect')
})
it('screen \'Remove token\' has correct label', async () => {
const title = await f.waitUntilShowUp(screens.removeToken.label)
assert.equal((await title.getText()).includes(screens.removeToken.labelText + token.ticker), true, 'label is incorrect')
})
it('button "No" bring back to "Main" screen', async () => {
const title = await f.waitUntilShowUp(screens.removeToken.title)
assert.equal(await title.getText(), screens.removeToken.titleText, 'title is incorrect')
const button = await f.waitUntilShowUp(screens.removeToken.buttons.no)
assert.notEqual(button, false, 'button \'No\' isn\'t displayed ')
assert.equal(await button.getText(), 'No', 'button has incorrect name')
await f.click(button)
const token = await f.waitUntilShowUp(screens.main.tokens.balance)
assert.notEqual(await token.getText(), '', 'token is disapeared after return from remove token screen ')
})
it('button "Yes" delete token', async () => {
const menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
const remove = await f.waitUntilShowUp(menus.token.remove)
await remove.click()
const title = await f.waitUntilShowUp(screens.removeToken.title)
assert.equal(await title.getText(), screens.removeToken.titleText, 'title is incorrect')
const button = await f.waitUntilShowUp(screens.removeToken.buttons.yes)
assert.notEqual(button, false, 'button \'Yes\' isn\'t displayed ')
assert.equal(await button.getText(), 'Yes', 'button has incorrect name')
await f.click(button)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from SOKOL network', async () => {
await f.setProvider(NETWORKS.SOKOL)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from KOVAN network', async () => {
await f.setProvider(NETWORKS.KOVAN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from ROPSTEN network', async () => {
await f.setProvider(NETWORKS.ROPSTEN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from MAINNET network', async () => {
await f.setProvider(NETWORKS.MAINNET)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from POA network', async () => {
await f.setProvider(NETWORKS.POA)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('check if token was removed from RINKEBY network', async () => {
await f.setProvider(NETWORKS.RINKEBY)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
})
}
module.exports = addCustomToken

View File

@ -0,0 +1,291 @@
const assert = require('assert')
const { screens, menus, elements, NETWORKS } = require('../elements')
const addTokeFromSearch = async (f) => {
const request = {
valid: 'cry',
invalid: 'zzz',
notExistingAddress: '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC',
}
const Qtum = {
name: 'Qtum (QTUM)',
address: '0x9a642d6b3368ddc662CA244bAdf32cDA716005BC',
}
describe('add Mainnet\'s tokens', function () {
it(' field \'Search\' is displayed', async () => {
await f.setProvider(NETWORKS.MAINNET)
await f.delay(2000)
const tab = await f.waitUntilShowUp(screens.main.tokens.menu)
await tab.click()
const button = await f.waitUntilShowUp(screens.main.tokens.buttonAdd2, 300)
await f.click(button)
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
assert.notEqual(field, false, 'field \'Search\' isn\'t displayed')
})
it('button \'Next\' is disabled if no tokens found', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
assert.equal(await button.isEnabled(), false, 'button is enabled')
assert.equal(await button.getText(), 'Next', 'button has incorrect name')
})
it('button \'Cancel\' is enabled and lead to main screen ', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.cancel)
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
assert.equal(await button.getText(), 'Cancel', 'button has incorrect name')
})
it('Search by name: searching result list is empty if request invalid', async () => {
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
await field.sendKeys(request.invalid)
const list = await f.waitUntilShowUp(screens.addToken.search.token.unselected, 20)
assert.equal(list, false, 'unexpected tokens are displayed')
})
it('Search by name: searching result list isn\'t empty ', async () => {
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
await f.clearField(field)
await field.sendKeys(request.valid)
await f.waitUntilShowUp(screens.addToken.search.token.unselected)
const list = await f.driver.findElements(screens.addToken.search.token.unselected)
assert.notEqual(list, 0, 'tokens aren\'t displayed')
})
it('Token\'s info contains name, symbol and picture ', async () => {
const tokens = await f.driver.findElements(screens.addToken.search.token.unselected)
const names = await f.driver.findElements(screens.addToken.search.token.name)
const icons = await f.driver.findElements(screens.addToken.search.token.icon)
assert.equal(tokens.length, names.length, 'some names are missed')
assert.equal(tokens.length, icons.length, 'some icons are missed')
})
it('button \'Next\' is disabled if no one token is selected', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
assert.equal(await button.isEnabled(), false, 'button is enabled')
})
it('user can select one token', async () => {
const token = await f.waitUntilShowUp(screens.addToken.search.token.unselected)
await token.click()
})
it('button \'Next\' is enabled if token is selected', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
assert.equal(await button.isEnabled(), true, 'button is disabled')
})
it('user can unselected token', async () => {
const token = await f.waitUntilShowUp(screens.addToken.search.token.selected)
await token.click()
})
it('button \'Next\' is disabled after token was unselected', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
assert.equal(await button.isEnabled(), false, 'button is enabled')
})
it('user can select two tokens', async () => {
await f.waitUntilShowUp(screens.addToken.search.token.unselected)
const tokensUnselected = await f.driver.findElements(screens.addToken.search.token.unselected)
await tokensUnselected[0].click()
await tokensUnselected[2].click()
const tokensSelected = await f.driver.findElements(screens.addToken.search.token.selected)
assert.equal(tokensSelected.length, 2, 'user can\'t select 2 tokens')
})
it('click button \'Next\' opens confirm screen ', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
await f.click(button)
const buttonAdd = await f.waitUntilShowUp(screens.addToken.search.confirm.button.add)
assert.notEqual(buttonAdd, false, 'failed to open screen confirmation')
})
it('confirm screen: two selected tokens are displayed and have correct parameters', async () => {
const tokens = await f.driver.findElements(screens.addToken.search.confirm.token.item)
assert.equal(tokens.length, 2, 'incorrect number of tokens are presented')
const names = await f.driver.findElements(screens.addToken.search.confirm.token.name)
const name0 = await names[0].getText()
const name1 = await names[1].getText()
assert.equal(name0.length > 10, true, 'empty token name')
assert.equal(name1.length > 10, true, 'empty token name')
await f.delay(2000)
const balances = await f.driver.findElements(screens.addToken.search.confirm.token.balance)
const balance0 = await balances[1].getText()
const balance1 = await balances[2].getText()
assert.equal(balance0, '0', 'balance isn\'t 0')
assert.equal(balance1, '0', 'balance isn\'t 0')
})
it('button \'Back\' is enabled and leads to previous screen ', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.confirm.button.back)
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
await f.click(button)
const fieldSearch = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
assert.notEqual(fieldSearch, false, 'add token screen didn\'t opened')
})
it('button \'Next\' is enabled if confirmation list isn\'t empty', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
assert.equal(await button.isEnabled(), true, 'button is disabled')
})
it('previous selected tokens remain selected after new search', async () => {
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
await f.clearField(field)
await field.sendKeys(request.valid)
await f.waitUntilShowUp(screens.addToken.search.token.selected)
const listSelected = await f.driver.findElements(screens.addToken.search.token.selected)
assert.equal(listSelected.length, 2, 'tokens are unselected')
})
it('user can unselect token', async () => {
const tokensUnselected = await f.driver.findElements(screens.addToken.search.token.unselected)
assert.notEqual(tokensUnselected.length, 0, 'all tokens are selected')
let tokensSelected = await f.driver.findElements(screens.addToken.search.token.selected)
await tokensSelected[0].click()
const old = tokensSelected.length
tokensSelected = await f.driver.findElements(screens.addToken.search.token.selected)
assert.equal(tokensSelected.length, old - 1, 'can\'t unselect token')
})
it('confirm screen: unselected token aren\'t displayed', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
await f.click(button)
await f.waitUntilShowUp(screens.addToken.search.confirm.token.item)
const tokens = await f.driver.findElements(screens.addToken.search.confirm.token.item)
assert.equal(tokens.length, 1, 'incorrect number of tokens are presented')
const back = await f.waitUntilShowUp(screens.addToken.search.confirm.button.back)
await f.click(back)
})
it('Search by contract address: searching result list is empty if address invalid ', async () => {
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
await field.sendKeys(request.notExistingAddress)
const list = await f.waitUntilShowUp(screens.addToken.search.token.unselected, 20)
assert.equal(list, false, 'unexpected tokens are displayed')
})
it('Search by valid contract address: searching result list contains one token ', async () => {
const field = await f.waitUntilShowUp(screens.addToken.search.fieldSearch)
await f.clearField(field)
await f.clearField(field)
await field.sendKeys(Qtum.address)
const token = await f.waitUntilShowUp(screens.addToken.search.token.unselected)
const list = await f.driver.findElements(screens.addToken.search.token.unselected)
assert.notEqual(list, 0, 'tokens aren\'t displayed')
await token.click()
})
it('Token\'s info contains correct name ', async () => {
const name = await f.waitUntilShowUp(screens.addToken.search.token.name)
assert.equal(await name.getText(), Qtum.name, 'incorrect token\'s name')
})
it('one more token added to confirmation list', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.button.next)
await f.click(button)
await f.waitUntilShowUp(screens.addToken.search.confirm.token.item)
const list = await f.driver.findElements(screens.addToken.search.confirm.token.item)
assert.equal(list.length, 2, 'token wasn\'t added')
})
it('button \'Add tokens\' is enabled and clickable', async () => {
const button = await f.waitUntilShowUp(screens.addToken.search.confirm.button.add)
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
await f.click(button)
const identicon = await f.waitUntilShowUp(screens.main.identicon)
assert.notEqual(identicon, false, 'main screen didn\'t opened')
})
it('all selected tokens are displayed on main screen', async () => {
await f.waitUntilShowUp(screens.main.tokens.token)
const tokens = await f.driver.findElements(screens.main.tokens.token)
assert.equal(tokens.length, 2, 'tokens weren\'t added')
})
it('correct value of counter of owned tokens', async () => {
const counter = await f.waitUntilShowUp(screens.main.tokens.counter)
assert.equal(await counter.getText(), 'You own 2 tokens', 'incorrect value of counter')
})
})
describe('Token should be displayed only for network, where it was added ', async () => {
it('token should not be displayed in POA network', async () => {
await f.setProvider(NETWORKS.POA)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in DAI network', async () => {
await f.setProvider(NETWORKS.DAI)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in SOKOL network', async () => {
await f.setProvider(NETWORKS.SOKOL)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in LOCALHOST network', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in ROPSTEN network', async () => {
await f.setProvider(NETWORKS.ROPSTEN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in KOVAN network', async () => {
await f.setProvider(NETWORKS.KOVAN)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
it('token should not be displayed in RINKEBY network', async () => {
await f.setProvider(NETWORKS.RINKEBY)
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
})
describe('remove Mainnet\'s tokens', function () {
it('remove tokens', async () => {
let menu
let button
let counter
let buttonYes
await f.setProvider(NETWORKS.MAINNET)
await f.waitUntilShowUp(elements.loader, 25)
await f.waitUntilDisappear(elements.loader, 50)
menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
button = await f.waitUntilShowUp(menus.token.remove)
await button.click()
buttonYes = await f.waitUntilShowUp(screens.removeToken.buttons.yes)
await buttonYes.click()
counter = await f.waitUntilShowUp(screens.main.tokens.counter)
assert.equal(await counter.getText(), 'You own 1 token', 'incorrect value of counter')
const tokensNumber = await f.driver.findElements(screens.main.tokens.token)
assert.equal(tokensNumber.length, 1, 'incorrect amount of token\'s is displayed')
menu = await f.waitUntilShowUp(menus.token.menu)
await menu.click()
button = await f.waitUntilShowUp(menus.token.remove)
await button.click()
buttonYes = await f.waitUntilShowUp(screens.removeToken.buttons.yes)
await buttonYes.click()
counter = await f.waitUntilShowUp(screens.main.tokens.counter)
assert.equal(await counter.getText(), 'No tokens found', 'incorrect value of counter')
assert.equal(await f.assertTokensNotDisplayed(), true, 'tokens are displayed')
})
})
}
module.exports = addTokeFromSearch

View File

@ -0,0 +1,174 @@
const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements')
const changePassword = async (f, password, newPassword) => {
let fieldNewPassword
let fieldConfirmNewPassword
let fieldOldPassword
let buttonYes
describe('Check screen "Settings" -> "Change password" ', async () => {
it('checks if current network name (localhost) is correct', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
const menu = await f.waitUntilShowUp(menus.sandwich.menu, 300)
await menu.click()
const settings = await f.waitUntilShowUp(menus.sandwich.settings)
await settings.click()
const field = await f.waitUntilShowUp(screens.settings.currentNetwork)
assert.equal(await field.getText(), 'http://localhost:8545', 'current network is incorrect')
})
it('error should not be displayed', async () => {
const error = await f.waitUntilShowUp(screens.settings.error, 10)
assert.equal(error, false, 'improper error is displayed')
})
it('checks if "Change password" button is present and enabled', async () => {
const menu = await f.waitUntilShowUp(menus.sandwich.menu, 300)
await menu.click()
const settings = await f.waitUntilShowUp(menus.sandwich.settings)
await settings.click()
await f.waitUntilShowUp(screens.settings.fieldNewRPC)
const buttons = await f.driver.findElements(screens.settings.buttons.changePassword)
await f.scrollTo(buttons[0])
assert.equal(buttons.length, 1, 'Button "Change password" is not present')
assert.equal(await buttons[0].getText(), 'Change password', 'button has incorrect name')
assert.equal(await buttons[0].isEnabled(), true, 'Button "Change password" is disabled')
await f.click(buttons[0])
})
it('screen has correct title', async () => {
const title = await f.waitUntilShowUp(screens.changePassword.title)
assert.equal(await title.getText(), screens.changePassword.titleText, '"Change password" screen contains incorrect title')
})
it('screen contains correct label', async () => {
await f.waitUntilShowUp(screens.changePassword.label)
const labels = await f.driver.findElements(screens.changePassword.label)
assert.equal(labels.length, 1, 'screen "Change password" doesn\'t contain label')
assert.equal(await labels[0].getText(), screens.changePassword.labelText, 'label contains incorrect title')
})
it('clicking the button "No" bring back to "Setting" screen ', async () => {
const button = await f.waitUntilShowUp(screens.changePassword.buttonNo)
assert.equal(await button.getText(), 'No', 'button has incorrect name')
await f.click(button)
const title = await f.waitUntilShowUp(screens.settings.title)
assert.equal(await title.getText(), screens.settings.titleText, 'button "No" doesnt open settings screen')
const buttonChangePass = await f.driver.findElement(screens.settings.buttons.changePassword)
await f.scrollTo(buttonChangePass)
await f.click(buttonChangePass)
})
})
describe('Validation of errors ', async () => {
before(async () => {
fieldOldPassword = await f.waitUntilShowUp(screens.changePassword.fieldOldPassword)
await fieldOldPassword.sendKeys(password)
fieldNewPassword = await f.waitUntilShowUp(screens.changePassword.fieldNewPassword)
fieldConfirmNewPassword = await f.waitUntilShowUp(screens.changePassword.fieldConfirmNewPassword)
buttonYes = await f.waitUntilShowUp(screens.changePassword.buttonYes)
})
it('error if new password shorter than 8 digits', async () => {
await fieldNewPassword.sendKeys(newPassword.short)
await fieldConfirmNewPassword.sendKeys(newPassword.short)
assert.equal(await buttonYes.getText(), 'Yes', 'button has incorrect name')
await f.click(buttonYes)
await f.delay(2000)
const errors = await f.driver.findElements(screens.changePassword.error)
assert.equal(errors.length > 0, true, 'error isn\'t displayed')
assert.equal(await errors[0].getText(), screens.changePassword.errorText.notLong, 'Error\'s text incorrect')
})
it('error if new password doesn\'t match confirmation', async () => {
await f.clearField(fieldNewPassword)
await f.clearField(fieldConfirmNewPassword)
await fieldNewPassword.sendKeys(newPassword.correct)
await fieldConfirmNewPassword.sendKeys(newPassword.incorrect)
await f.click(buttonYes)
await f.delay(2000)
const errors = await f.driver.findElements(screens.changePassword.error)
assert.equal(errors.length > 0, true, 'error isn\'t displayed')
assert.equal(await errors[0].getText(), screens.changePassword.errorText.dontMatch, 'Error\'s text incorrect')
})
it('error if new password match old password', async () => {
await f.clearField(fieldNewPassword)
await f.clearField(fieldConfirmNewPassword)
await fieldNewPassword.sendKeys(password)
await fieldConfirmNewPassword.sendKeys(password)
await f.click(buttonYes)
await f.delay(2000)
const errors = await f.driver.findElements(screens.changePassword.error)
assert.equal(errors.length > 0, true, 'error isn\'t displayed')
assert.equal(await errors[0].getText(), screens.changePassword.errorText.differ, 'Error\'s text incorrect')
})
it('error if old password incorrect', async () => {
await f.clearField(fieldOldPassword)
await fieldOldPassword.sendKeys(newPassword.incorrect)
await f.click(buttonYes)
await f.click(buttonYes)
await f.delay(2000)
const errors = await f.driver.findElements(screens.changePassword.error)
assert.equal(errors.length > 0, true, 'error isn\'t displayed')
assert.equal(await errors[0].getText(), screens.changePassword.errorText.incorrectPassword, 'Error\'s text incorrect')
})
it('no errors if old, new, confirm new passwords are correct; user can change password', async () => {
await f.clearField(fieldNewPassword)
await f.clearField(fieldOldPassword)
await f.clearField(fieldConfirmNewPassword)
await fieldOldPassword.sendKeys(password)
await fieldNewPassword.sendKeys(newPassword.correct)
await fieldConfirmNewPassword.sendKeys(newPassword.correct)
await f.click(buttonYes)
await f.waitUntilShowUp(screens.settings.buttons.changePassword, 25)
const buttons = await f.driver.findElements(screens.settings.buttons.changePassword)
assert.equal(buttons.length, 1, 'Button "Change password" is not present')
assert.equal(await buttons[0].isEnabled(), true, 'Button "Change password" is disabled')
})
})
describe('Check if new password is accepted', async () => {
it('user can log out', async () => {
const menu = await f.waitUntilShowUp(menus.sandwich.menu)
await menu.click()
const itemLogOut = await f.waitUntilShowUp(menus.sandwich.logOut)
await itemLogOut.click()
const field = await f.waitUntilShowUp(screens.lock.fieldPassword)
assert.notEqual(field, false, 'password box isn\'t present after logout')
})
it('can\'t login with old password', async () => {
const field = await f.waitUntilShowUp(screens.lock.fieldPassword)
await field.sendKeys(password)
const button = await f.waitUntilShowUp(screens.lock.buttonLogin)
await f.click(button)
const error = await f.waitUntilShowUp(screens.lock.error)
assert.notEqual(error, false, 'error isn\'t displayed if password incorrect')
assert.equal(await error.getText(), screens.lock.errorText, 'error\'s text incorrect')
})
it('accepts new password after lock', async () => {
const field = await f.waitUntilShowUp(screens.lock.fieldPassword)
await f.clearField(field)
await field.sendKeys(newPassword.correct)
const button = await f.waitUntilShowUp(screens.lock.buttonLogin)
await f.click(button)
await f.waitUntilShowUp(screens.main.buttons.buy)
const buttons = await f.driver.findElements(screens.main.buttons.buy)
assert.equal(buttons.length, 1, 'main screen isn\'t displayed')
assert.equal(await buttons[0].getText(), 'Buy', 'button has incorrect name')
password = newPassword.correct
})
})
}
module.exports = changePassword

View File

@ -0,0 +1,68 @@
const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements')
const eventsEmitter = 'https://vbaranov.github.io/event-listener-dapp/'
const checkEmittedEvents = async (f, account1, account2) => {
it('emit event', async () => {
await f.setProvider(NETWORKS.SOKOL)
let account
if (process.env.SELENIUM_BROWSER === 'chrome') {
account = account1
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
account = account2
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account2)
await item.click()
}
const balanceField = await f.waitUntilShowUp(screens.main.balance)
await f.delay(2000)
const balance = await balanceField.getText()
console.log('Account = ' + account)
console.log('Balance = ' + balance)
assert.equal(parseFloat(balance) > 0.001, true, 'Balance of account ' + account + ' TOO LOW !!! Please refill with Sokol eth!!!!')
await f.driver.get(eventsEmitter)
const button = await f.waitUntilShowUp(screens.eventsEmitter.button)
await button.click()
await f.delay(1000)
})
it('confirms transaction in MetaMask popup', async () => {
const windowHandles = await f.driver.getAllWindowHandles()
await f.driver.switchTo().window(windowHandles[windowHandles.length - 1])
await f.delay(5000)
const gasPrice = await f.waitUntilShowUp(screens.confirmTransaction.fields.gasPrice)
await gasPrice.sendKeys('10')
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.submit)
await f.click(button)
})
it('check number of events', async () => {
const windowHandles = await f.driver.getAllWindowHandles()
await f.driver.switchTo().window(windowHandles[0])
await f.delay(5000)
const event = await f.waitUntilShowUp(screens.eventsEmitter.event, 600)
const events = await f.driver.findElements(screens.eventsEmitter.event)
console.log('number of events = ' + events.length)
if (!event) console.log("event wasn't created or transaction failed".toUpperCase())
else {
const events = await f.driver.findElements(screens.eventsEmitter.event)
assert.equal(events.length, 1, 'More than 1 event was fired: ' + events.length + ' events')
}
})
it('open app', async () => {
if (process.env.SELENIUM_BROWSER === 'chrome') {
await f.driver.get(`chrome-extension://${f.extensionId}/popup.html`)
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
await f.driver.get(`moz-extension://${f.extensionId}/popup.html`)
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account1)
await item.click()
}
})
}
module.exports = checkEmittedEvents

View File

@ -0,0 +1,101 @@
const assert = require('assert')
const { menus, screens } = require('../elements')
const connectHDWallet = async (f) => {
it("Account menu contais item 'Connect HD wallet'", async () => {
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
await f.waitUntilShowUp(menus.account.item)
const items = await f.driver.findElements(menus.account.item)
await f.delay(500)
assert.equal(await items[4].getText(), 'Connect hardware wallet', "item's text incorrect")
await items[4].click()
})
it("Opens screen 'Connect HD wallet',title is correct", async () => {
const title = await f.waitUntilShowUp(screens.hdWallet.title)
assert.equal(await title.getText(), 'Connect to hardware wallet', "item's text incorrect")
})
if (process.env.SELENIUM_BROWSER === 'chrome') {
it("Button 'Connect' disabled by default", async () => {
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.disabled)
assert.notEqual(button, false, "button isn't displayed")
assert.equal(await button.getText(), 'CONNECT', 'button has incorrect text')
})
it('Ledger image is displayed', async () => {
const image = await f.waitUntilShowUp(screens.hdWallet.image)
assert.notEqual(image, false, "ledger's image isn't displayed")
const src = await image.getAttribute('src')
assert.equal(src.includes('images/ledger-logo.svg'), true, 'Ledger has incorrect image')
})
it('Trezor image is displayed', async () => {
const images = await f.driver.findElements(screens.hdWallet.image)
assert.notEqual(images[1], false, "trezor's image isn't displayed")
const src = await images[1].getAttribute('src')
assert.equal(src.includes('images/trezor-logo.svg'), true, 'Trezor has incorrect image')
})
it("Button 'Connect' enabled if Trezor selected", async () => {
const images = await f.driver.findElements(screens.hdWallet.image)
await images[1].click()
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
assert.equal(await button.isEnabled(), true, 'button is disabled')
})
it("Button 'Connect' enabled if Ledger selected", async () => {
const images = await f.driver.findElements(screens.hdWallet.image)
await images[0].click()
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
assert.equal(await button.isEnabled(), true, 'button is disabled')
})
it('Only one device can be selected', async () => {
const selected = await f.driver.findElements(screens.hdWallet.imageSelected)
assert.equal(await selected.length, 1, 'more than one device is selected')
})
it('Error message if connect Ledger', async () => {
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
await button.click()
const error = await f.waitUntilShowUp(screens.hdWallet.error)
const shouldBe = "TransportError: U2F browser support is needed for Ledger. Please use Chrome, Opera or Firefox with a U2F extension. Also make sure you're on an HTTPS connection"
assert.equal(await error.getText(), shouldBe, 'error has incorrect text')
})
it('Popup opens if connect Trezor', async () => {
const images = await f.driver.findElements(screens.hdWallet.image)
await images[1].click()
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
await button.click()
await f.delay(2000)
const allHandles = await f.driver.getAllWindowHandles()
assert.equal(allHandles.length, 2, "popup isn't opened")
f.driver.switchTo().window(allHandles[1])
await f.delay(2000)
f.driver.close()
f.driver.switchTo().window(allHandles[0])
await f.delay(2000)
assert.equal(allHandles.length, 2, "popup isn't opened")
await f.switchToFirstPage()
await f.driver.navigate().refresh()
})
}
it('Button arrow leads to main screen', async () => {
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
await f.waitUntilShowUp(menus.account.item)
const items = await f.driver.findElements(menus.account.item)
await f.delay(500)
await items[4].click()
const arrow = await f.waitUntilShowUp(screens.hdWallet.buttonArrow)
await arrow.click()
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
assert.notEqual(ident, false, "main screen isn't opened")
})
}
module.exports = connectHDWallet

View File

@ -0,0 +1,150 @@
const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements')
const customRPC = async (f) => {
const invalidStringUrl = 'http://lwkdfowi**&#v er'
const urlWithoutHttp = 'infura.com'
const invalidEndpoint = 'http://abrakadabrawdjkwjeciwkasuhlvflwe.com'
const correctRpcUrl = 'https://poa.infura.io/test'
it('switches to settings screen through menu \'Network -> Custom RPC\'', async function () {
await f.setProvider(NETWORKS.CUSTOM)
const settings = await f.waitUntilShowUp(screens.settings.title)
assert.equal(await settings.getText(), screens.settings.titleText, 'inappropriate screen is opened')
})
it('error message if new Rpc url is invalid', async function () {
const field = await f.waitUntilShowUp(screens.settings.fieldNewRPC)
await field.sendKeys(invalidStringUrl)
const button = await f.waitUntilShowUp(screens.settings.buttonSave)
assert.equal(await button.getText(), 'Save', 'button has incorrect name')
await f.click(button)
await f.delay(1000)
assert.equal(await f.waitUntilShowUp(screens.settings.buttons.delete, 5), false, 'invalid Rpc was added')
const errors = await f.driver.findElements(screens.settings.error)
assert.equal(errors.length, 1, 'error isn\'t displayed if Rpc url incorrect')
assert.equal(await errors[0].getText(), screens.settings.errors.invalidRpcUrl, 'error\'s text incorrect')
})
it('error message if new Rpc url has no HTTP/HTTPS prefix', async function () {
const fieldRpc = await f.driver.findElement(screens.settings.fieldNewRPC)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await fieldRpc.sendKeys(urlWithoutHttp)
const button = await f.waitUntilShowUp(screens.settings.buttonSave)
await f.click(button)
await f.delay(1000)
assert.equal(await f.waitUntilShowUp(screens.settings.buttons.delete, 5), false, 'invalid Rpc was added')
const errors = await f.driver.findElements(screens.settings.error)
assert.equal(errors.length, 1, 'error isn\'t displayed if Rpc url incorrect')
assert.equal(await errors[0].getText(), screens.settings.errors.invalidHTTP, 'error\'s text incorrect')
})
it('error message if Rpc doesn\'t exist', async function () {
const fieldRpc = await f.driver.findElement(screens.settings.fieldNewRPC)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await fieldRpc.sendKeys(invalidEndpoint)
const button = await f.waitUntilShowUp(screens.settings.buttonSave)
await f.click(button)
await f.delay(1000)
assert.equal(await f.waitUntilShowUp(screens.settings.buttons.delete, 5), false, 'invalid Rpc was added')
await f.waitUntilShowUp(screens.settings.error)
const errors = await f.driver.findElements(screens.settings.error)
assert.equal(errors.length, 1, 'error isn\'t displayed if Rpc url incorrect')
assert.equal(await errors[0].getText(), screens.settings.errors.invalidRpcEndpoint, 'error\'s text incorrect')
})
it('user can add valid custom rpc', async function () {
const fieldRpc = await f.driver.findElement(screens.settings.fieldNewRPC)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await fieldRpc.sendKeys(correctRpcUrl + 0)
await f.driver.findElement(screens.settings.buttonSave).click()
await f.delay(10000)
const customUrlElement = await f.waitUntilShowUp(screens.settings.currentNetwork)
assert.equal(await customUrlElement.getText(), correctRpcUrl + 0, 'Added Url doesn\'t match')
})
it('new added Rpc displayed in network dropdown menu', async function () {
let menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
const item = await f.waitUntilShowUp(menus.networks.addedCustomRpc)
assert.equal(await item.getText(), correctRpcUrl + 0, 'Added custom Url isn\'t displayed ')
menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
})
it('user can add four more valid custom rpc', async function () {
const fieldRpc = await f.waitUntilShowUp(screens.settings.fieldNewRPC)
const customUrlElement = await f.waitUntilShowUp(screens.settings.currentNetwork)
for (let i = 1; i < 5; i++) {
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await f.clearField(fieldRpc)
await fieldRpc.sendKeys(correctRpcUrl + i)
await f.driver.findElement(screens.settings.buttonSave).click()
await f.delay(5000)
assert.equal(await customUrlElement.getText(), correctRpcUrl + i, '#' + i + ': Current RPC field contains incorrect URL')
}
})
it('new added Rpc displayed in network dropdown menu', async function () {
let menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
await f.waitUntilShowUp(menus.networks.addedCustomRpc)
const items = await f.driver.findElements(menus.networks.addedCustomRpc)
assert.equal(items.length, 5, 'Incorrect number of added RPC')
menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
})
it('click button \'Delete\' opens screen \'Delete Custom RPC\'', async function () {
await f.delay(1000)
const button = await f.waitUntilShowUp(screens.settings.buttons.delete, 10)
assert.equal(await button.getText(), 'Delete', 'button has incorrect name')
await f.click(button)
const title = await f.waitUntilShowUp(screens.settings.title)
assert.equal(await title.getText(), screens.deleteCustomRPC.titleText, 'inappropriate screen is opened')
})
it('click button \'No\' opens screen \'Settings\'', async function () {
const button = await f.waitUntilShowUp(screens.deleteCustomRPC.buttons.no)
assert.equal(await button.getText(), 'No', 'button has incorrect name')
await f.click(button)
const title = await f.waitUntilShowUp(screens.settings.title)
assert.equal(await title.getText(), screens.settings.titleText, 'inappropriate screen is opened')
})
it('user able to delete custom rpc', async function () {
const buttonDelete = await f.waitUntilShowUp(screens.settings.buttons.delete, 25)
await f.click(buttonDelete)
const yesButton = await f.waitUntilShowUp(screens.deleteCustomRPC.buttons.yes)
assert.equal(await yesButton.getText(), 'Yes')
await f.click(yesButton)
const title = await f.waitUntilShowUp(screens.settings.title)
assert.equal(await title.getText(), screens.settings.titleText, 'inappropriate screen is opened')
})
it('deleted custom rpc isn\'t displayed in \'Settings\' screen', async function () {
const currentNetwork = await f.waitUntilShowUp(screens.settings.currentNetwork)
assert.equal(await currentNetwork.getText(), 'POA Network', 'custom Rpc is displayed after deletion')
})
it('deleted custom rpc isn\'t displayed in network dropdown menu', async function () {
await f.delay(2000)
let menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
await f.waitUntilShowUp(menus.networks.addedCustomRpc, 20)
const items = await f.driver.findElements(menus.networks.addedCustomRpc)
assert.equal(items.length, 4, 'deleted custom rpc is displayed in network dropdown menu')
menu = await f.waitUntilShowUp(screens.main.network)
await menu.click()
})
}
module.exports = customRPC

View File

@ -0,0 +1,57 @@
const assert = require('assert')
const { menus, screens } = require('../elements')
const { account } = menus
const { deleteImportedAccount: deleteImportedAccountScr, settings } = screens
const deleteImportedAccount = async (f) => {
it('Open delete imported account screen', async function () {
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
const item = await f.waitUntilShowUp(account.delete)
await item.click()
const deleteImportedAccountTitle = await f.waitUntilShowUp(deleteImportedAccountScr.title)
assert.equal(await deleteImportedAccountTitle.getText(), deleteImportedAccountScr.titleText)
})
it("Can't remove imported account with 'No' button", async function () {
const button = await f.waitUntilShowUp(deleteImportedAccountScr.buttons.no)
assert.equal(await button.getText(), 'No', 'button has incorrect name')
await f.click(button)
const settingsTitle = await f.waitUntilShowUp(settings.title)
assert.equal(await settingsTitle.getText(), 'Settings')
// check, that imported account still exists
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
await f.delay(2000)
const label = await f.waitUntilShowUp(account.label)
assert.equal(await label.getText(), 'IMPORTED')
})
it('Open delete imported account screen again', async function () {
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
await f.delay(2000)
await menu.click()
await f.waitUntilShowUp(account.delete)
const buttons = await f.driver.findElements(account.delete)
assert.notEqual(buttons[0], false, "icon 'remove' isn't displayed")
await buttons[0].click()
})
it("Remove imported account with 'Yes' button", async function () {
const button = await f.waitUntilShowUp(deleteImportedAccountScr.buttons.yes)
assert.equal(await button.getText(), 'Yes', 'button has incorrect name')
await f.click(button)
const settingsTitle = await f.waitUntilShowUp(settings.title)
assert.equal(await settingsTitle.getText(), 'Settings', "screen 'Settings' has incorrect title")
// check, that imported account is removed
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
await f.delay(3000)
const label = await f.waitUntilShowUp(account.label, 25)
assert.equal(label, false, "account isn't deleted")
await menu.click()
})
}
module.exports = deleteImportedAccount

View File

@ -0,0 +1,84 @@
const assert = require('assert')
const clipboardy = require('clipboardy')
const { screens, menus } = require('../elements')
const exportPrivateKey = async (f, password) => {
it('open dialog', async () => {
await f.driver.navigate().refresh()
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.dot.exportPR)
await item.click()
})
it('warning is displayed', async () => {
await f.waitUntilShowUp(screens.exportPR.error)
const error = await f.driver.findElements(screens.exportPR.error)
assert.equal(error.length, 1, 'warning isn\'t present')
assert.equal(await error[0].getText(), screens.exportPR.warningText, 'warning\'s text incorrect')
})
it('button \'Cancel\' leads back to main screen', async () => {
const button = await f.waitUntilShowUp(screens.exportPR.button.cancel)
assert.equal(await button.getText(), 'Cancel', 'button has incorrect name')
await f.click(button)
const field = await f.waitUntilShowUp(screens.exportPR.fieldPassword, 20)
assert.equal(field, false, 'field \'password\' is displayed after closing')
})
it('error message if password incorrect', async () => {
await f.driver.navigate().refresh()
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.dot.exportPR)
await item.click()
const field = await f.waitUntilShowUp(screens.exportPR.fieldPassword)
await field.sendKeys('abrakadabr')
const button = await f.waitUntilShowUp(screens.exportPR.button.submit)
assert.equal(await button.getText(), 'Submit', 'button has incorrect name')
await f.click(button)
await f.delay(500)
const error = await f.driver.findElements(screens.exportPR.error)
assert.equal(error.length, 2, 'warning isn\'t present')
assert.equal(await error[1].getText(), screens.exportPR.errorText, 'error\'s text incorrect')
})
it('private key is shown if password correct', async () => {
const field = await f.waitUntilShowUp(screens.exportPR.fieldPassword)
await f.clearField(field)
await field.sendKeys(password)
const button = await f.waitUntilShowUp(screens.exportPR.button.submit)
await f.click(button)
const key = await f.waitUntilShowUp(screens.yourPR.key)
const pr = await key.getText()
assert.equal(pr.length, 32 * 2, 'private key isn\'t displayed')
})
it('icon copy cliboard is displayed and clickable', async () => {
await f.waitUntilShowUp(screens.yourPR.copy)
const icons = await f.driver.findElements(screens.yourPR.copy)
assert.notEqual(icons[1], false, 'icon copy isn\'t displayed')
await icons[1].click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.length, 64, "private key wasn't copied to clipboard")
})
it('file loaded if click button \'Save\' ', async () => {
const button = await f.waitUntilShowUp(screens.yourPR.button.save)
assert.equal(await button.getText(), 'Save as File', 'button has incorrect name')
assert.notEqual(button, false, 'button \'Save\' isn\'t displayed')
})
it('button \'Done\' leads back to main screen', async () => {
const button = await f.waitUntilShowUp(screens.yourPR.button.done)
await f.click(button)
const field = await f.waitUntilShowUp(screens.yourPR.key, 20)
assert.equal(field, false, 'screen \'Your PR\' is displayed after closing')
await f.driver.navigate().refresh()
})
}
module.exports = exportPrivateKey

View File

@ -0,0 +1,69 @@
const assert = require('assert')
const { menus, screens, elements, NETWORKS } = require('../elements')
const { account } = menus
const { main: {
tokens: tokensEl,
balance: balanceEl,
},
importAccounts,
} = screens
const addr = '0xf4702CbA917260b2D6731Aea6385215073e8551b'
const addrPrivKey = '76bd0ced0a47055bb5d060e1ae4a8cb3ece658d668823e250dae6e79d3ab4435'
const importAccount = async (f) => {
it('Open import account menu', async () => {
await f.setProvider(NETWORKS.POA)
await f.delay(2000)
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
const item = await f.waitUntilShowUp(account.import)
await item.click()
const importAccountTitle = await f.waitUntilShowUp(importAccounts.title)
assert.equal(await importAccountTitle.getText(), importAccounts.textTitle)
})
it('Imports account', async () => {
const privateKeyBox = await f.waitUntilShowUp(importAccounts.fieldPrivateKey)
await privateKeyBox.sendKeys(addrPrivKey)
const button = await f.waitUntilShowUp(importAccounts.buttonImport)
assert.equal(await button.getText(), 'Import', 'button has incorrect name')
await f.click(button)
const menu = await f.waitUntilShowUp(account.menu)
await menu.click()
await f.waitUntilShowUp(account.label)
const labels = await f.driver.findElements(account.label)
const label = labels[0]
assert.equal(await label.getText(), 'IMPORTED')
await menu.click()
})
it('Auto-detect tokens for POA core network ', async () => {
// await setProvider(NETWORKS.POA)
const tab = await f.waitUntilShowUp(tokensEl.menu)
await tab.click()
const balance = await f.waitUntilShowUp(tokensEl.balance)
console.log(await balance.getText())
assert.equal(await balance.getText(), '1 DOPR', 'token isnt\' auto-detected')
})
it.skip('Auto-detect tokens for MAIN core network ', async () => {
await f.setProvider(NETWORKS.MAINNET)
await f.waitUntilShowUp(elements.loader, 25)
await f.waitUntilDisappear(elements.loader, 25)
const balance = await f.waitUntilShowUp(tokensEl.balance)
console.log(await balance.getText())
assert.equal(await balance.getText(), '0.001 WETH', 'token isnt\' auto-detected')
})
it('Check Sokol balance', async () => {
await f.setProvider(NETWORKS.POA)
await f.delay(2000)
const balanceField = await f.waitUntilShowUp(balanceEl)
const balance = await balanceField.getText()
console.log(`Account = ${addr}`)
console.log('Balance = ' + balance)
assert.equal(parseFloat(balance) > 0.001, true, `Balance of account ${addr} TOO LOW !!! Please refill with Sokol eth!!!!`)
})
}
module.exports = importAccount

View File

@ -0,0 +1,862 @@
const assert = require('assert')
const clipboardy = require('clipboardy')
const { menus, screens, elements, NETWORKS } = require('../elements')
let abiClipboard
const importContractAccount = async (f, account1, getCreatedAccounts) => {
describe('Proxy contract', async () => {
const proxyContract = '0x0518ac3db78eb326f42dbcfb4b2978e8059989a5'
const proxyABI = [{'constant': true, 'inputs': [], 'name': 'proxyOwner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'version', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'version', 'type': 'string'}, {'name': 'implementation', 'type': 'address'}], 'name': 'upgradeTo', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'implementation', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'upgradeabilityOwner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'version', 'type': 'string'}, {'name': 'implementation', 'type': 'address'}, {'name': 'data', 'type': 'bytes'}], 'name': 'upgradeToAndCall', 'outputs': [], 'payable': true, 'stateMutability': 'payable', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'newOwner', 'type': 'address'}], 'name': 'transferProxyOwnership', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'payable': true, 'stateMutability': 'payable', 'type': 'fallback'}, {'anonymous': false, 'inputs': [{'indexed': false, 'name': 'previousOwner', 'type': 'address'}, {'indexed': false, 'name': 'newOwner', 'type': 'address'}], 'name': 'ProxyOwnershipTransferred', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': false, 'name': 'version', 'type': 'string'}, {'indexed': true, 'name': 'implementation', 'type': 'address'}], 'name': 'Upgraded', 'type': 'event'}] // eslint-disable-line no-unused-vars
const joinedABI = [{'constant': true, 'inputs': [], 'name': 'proxyOwner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'version', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'version', 'type': 'string'}, {'name': 'implementation', 'type': 'address'}], 'name': 'upgradeTo', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'implementation', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'upgradeabilityOwner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'version', 'type': 'string'}, {'name': 'implementation', 'type': 'address'}, {'name': 'data', 'type': 'bytes'}], 'name': 'upgradeToAndCall', 'outputs': [], 'payable': true, 'stateMutability': 'payable', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'newOwner', 'type': 'address'}], 'name': 'transferProxyOwnership', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'payable': true, 'stateMutability': 'payable', 'type': 'fallback'}, {'anonymous': false, 'inputs': [{'indexed': false, 'name': 'previousOwner', 'type': 'address'}, {'indexed': false, 'name': 'newOwner', 'type': 'address'}], 'name': 'ProxyOwnershipTransferred', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': false, 'name': 'version', 'type': 'string'}, {'indexed': true, 'name': 'implementation', 'type': 'address'}], 'name': 'Upgraded', 'type': 'event'}, {'constant': true, 'inputs': [], 'name': 'desc', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'methodFromImplementation', 'outputs': [{'name': 'yep', 'type': 'bool'}], 'payable': false, 'stateMutability': 'pure', 'type': 'function'}]
describe('imports ABI of proxy and implementation together', async () => {
it('opens import account menu', async () => {
await f.setProvider(NETWORKS.SOKOL)
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.account.import2)
await item.click()
const importAccountTitle = await f.waitUntilShowUp(screens.importAccounts.title)
assert.equal(await importAccountTitle.getText(), screens.importAccounts.textTitle)
})
it("Select type 'Proxy'", async () => {
await f.delay(1000)
const field = await f.waitUntilShowUp(screens.importAccounts.selectArrow)
await field.click()
const item = await f.waitUntilShowUp(screens.importAccounts.itemProxyContract)
await item.click()
})
it("Fill 'Address' with valid proxy contract , SOKOL", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractAddress)
await f.clearField(field, 100)
await field.sendKeys(proxyContract)
})
it('ABI of Proxy + Implementation is fetched and matches the pattern', async () => {
await f.delay(5000)
const field = await f.waitUntilShowUp(screens.importAccounts.contractABI)
abiClipboard = await field.getText()
console.log(abiClipboard)
assert.deepEqual(JSON.parse(abiClipboard), joinedABI, "ABI isn't fetched")
})
it("Click button 'Import', main screen opens", async () => {
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
await f.click(button)
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
assert.notEqual(ident, false, "main screen isn't opened")
})
})
describe("Check 3dots menu for 'Proxy' account", () => {
it('open 3dots menu', async () => {
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
await f.waitUntilShowUp(menus.dot.item)
const items = await f.driver.findElements(menus.dot.item)
assert.equal(items.length, 5, '3dot menu has incorrect number of items')
})
it('Check text of items', async () => {
const items = await f.driver.findElements(menus.dot.item)
assert.equal(await items[0].getText(), 'View on block explorer', '1st item has incorrect text')
assert.equal(await items[1].getText(), 'Show QR Code', '2nd item has incorrect text')
assert.equal(await items[2].getText(), 'Copy address to clipboard', '3d item has incorrect text')
assert.equal(await items[3].getText(), 'Copy ABI to clipboard', '4th item has incorrect text')
assert.equal(await items[4].getText(), 'Update implementation ABI', '5th item has incorrect text')
})
it("Click 'Update implementation ABI'", async () => {
const items = await f.driver.findElements(menus.dot.item)
await items[4].click()
const menu = await f.waitUntilShowUp(menus.dot.item, 20)
assert.equal(menu, false, "3dot menu wasn't closed")
})
})
describe("Remove imported 'Proxy' account", async () => {
it("Label 'PROXY' present", async () => {
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
await f.delay(2000)
await f.waitUntilShowUp(menus.account.label)
const labels = await f.driver.findElements(menus.account.label)
const label = labels[1]
assert.equal(await label.getText(), 'PROXY', 'label incorrect')
})
it('Delete imported account', async () => {
await f.waitUntilShowUp(menus.account.delete)
const items = await f.driver.findElements(menus.account.delete)
await items[1].click()
const button = await f.waitUntilShowUp(screens.deleteImportedAccount.buttons.yes)
await button.click()
const buttonArrow = await f.waitUntilShowUp(screens.settings.buttons.arrow)
await buttonArrow.click()
const identicon = await f.waitUntilShowUp(screens.main.identicon)
assert.notEqual(identicon, false, 'main screen didn\'t opened')
})
})
})
describe('Simple contract', async () => {
const contractSokol = '0x215b2ab35749e5a9f3efe890de602fb9844e842f'
console.log('Contract ' + contractSokol + ' , Sokol')
const wrongAddress = '0xB87b6077D59B01Ab9fa8cd5A1A21D02a4d60D35'
const notContractAddress = '0x56B2e3C3cFf7f3921Dc2e0F8B8e20d1eEc29216b'
describe('Import Contract', async () => {
it('opens import account menu', async () => {
await f.setProvider(NETWORKS.ROPSTEN)
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
const item = await f.waitUntilShowUp(menus.account.import2)
await item.click()
const importAccountTitle = await f.waitUntilShowUp(screens.importAccounts.title)
assert.equal(await importAccountTitle.getText(), screens.importAccounts.textTitle)
})
it("Warning's text is correct", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.warning)
assert.equal(await field.getText(), 'Imported accounts will not be associated with your originally created Nifty Wallet account seedphrase.', "incorrect warning's text")
})
it("Select type 'Contract'", async () => {
await f.delay(1000)
const field = await f.waitUntilShowUp(screens.importAccounts.selectArrow)
await field.click()
await f.delay(2000)
const item = await f.waitUntilShowUp(screens.importAccounts.itemContract)
await item.click()
})
it("Field 'Address' is displayed", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractAddress)
assert.notEqual(field, false, "field 'Address' isn't displayed")
await field.sendKeys(wrongAddress)
})
it("Button 'Import' is displayed", async () => {
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
assert.notEqual(button, false, "button 'Import' isn't displayed")
assert.equal(await button.getText(), 'Import', 'wrong name of button')
})
it("Button 'Import' is disabled if incorrect address", async () => {
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
assert.equal(await button.isEnabled(), false, 'button enabled')
})
it("Field 'ABI' is displayed", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractABI)
assert.notEqual(field, false, "field 'ABI' isn't displayed")
})
it("Field 'ABI' is empty if contract isn't verified in current network", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractABI)
assert.equal(await field.getText(), '', "field 'ABI' isn't displayed")
})
it("Fill 'Address' with not contract address , SOKOL", async () => {
await f.setProvider(NETWORKS.SOKOL)
const field = await f.waitUntilShowUp(screens.importAccounts.contractAddress)
await f.clearField(field, 100)
await field.sendKeys(notContractAddress)
})
it("Button 'Import' is disabled if not contract address", async () => {
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
assert.equal(await button.isEnabled(), false, 'button enabled')
})
it("Fill 'Address' with valid contract , SOKOL", async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractAddress)
await f.clearField(field, 100)
await field.sendKeys(contractSokol)
})
it("Button 'Import' is enabled if contract address is correct", async () => {
await f.delay(5000)
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
assert.equal(await button.isEnabled(), true, 'button enabled')
})
it('ABI is fetched ', async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.contractABI)
abiClipboard = await field.getText()
assert.equal(abiClipboard.length, 4457, "ABI isn't fetched")
})
it('icon copy is displayed for ABI ', async () => {
const field = await f.waitUntilShowUp(screens.importAccounts.iconCopy)
assert.notEqual(field, false, "icon copy isn't displayed")
await field.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text, abiClipboard, "address account wasn't copied to clipboard")
})
it("Click button 'Import', main screen opens", async () => {
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
await f.click(button)
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
assert.notEqual(ident, false, "main screen isn't opened")
})
})
describe("Check 3dots menu for 'Contract' account", () => {
it('open 3dots menu', async () => {
const menu = await f.waitUntilShowUp(menus.dot.menu)
await menu.click()
await f.waitUntilShowUp(menus.dot.item)
const items = await f.driver.findElements(menus.dot.item)
assert.equal(items.length, 4, '3dot menu has incorrect number of items')
})
it('Check text of items', async () => {
const items = await f.driver.findElements(menus.dot.item)
assert.equal(await items[0].getText(), 'View on block explorer', '1st item has incorrect text')
assert.equal(await items[1].getText(), 'Show QR Code', '2nd item has incorrect text')
assert.equal(await items[2].getText(), 'Copy address to clipboard', '3d item has incorrect text')
assert.equal(await items[3].getText(), 'Copy ABI to clipboard', '4th item has incorrect text')
})
it("Click 'Copy ABI'", async () => {
const items = await f.driver.findElements(menus.dot.item)
await items[3].click()
const menu = await f.waitUntilShowUp(menus.dot.item, 20)
assert.equal(menu, false, "3dot menu wasn't closed")
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text, abiClipboard, "ABI wasn't copied to clipboard")
})
})
describe('Execute Method screen', () => {
const notContractAddress = '0x56B2e3C3cFf7f3921Dc2e0F8B8e20d1eEc29216b'
describe("Check UI and button's functionality", () => {
it("Click button 'Execute method'", async () => {
await f.driver.navigate().refresh()
await f.delay(2000)
const button = await f.waitUntilShowUp(screens.executeMethod.buttonExecuteMethod)
assert.notEqual(button, false, "button doesn't displayed")
assert.equal(await button.getText(), 'Execute methods', 'button has incorrect name')
await button.click()
})
it('title is displayed and correct', async () => {
const title = await f.waitUntilShowUp(screens.executeMethod.title)
assert.notEqual(title, false, 'title isn\'t displayed')
assert.equal(await title.getText(), screens.executeMethod.titleText, 'incorrect text')
})
it('Click arrow button leads to main screen', async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonArrow)
await f.click(button)
const identicon = await f.waitUntilShowUp(screens.main.identicon, 40)
assert.notEqual(identicon, false, "main screen isn't opened")
})
})
describe('Check output for data type : ADDRESS', () => {
const address = '0x56B2e3C3cFf7f3921Dc2e0F8B8e20d1eEc29216b'
it("Click button 'Execute method'", async () => {
await f.driver.navigate().refresh()
await f.delay(2000)
const button = await f.waitUntilShowUp(screens.executeMethod.buttonExecuteMethod)
assert.notEqual(button, false, "button doesn't displayed")
assert.equal(await button.getText(), 'Execute methods', 'button has incorrect name')
await button.click()
})
it("Select method 'returnAddress'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[3].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it("Button 'Call data' is displayed and disabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), false, "Button 'Call data' is enabled")
})
it("Fill out input field 'Address'", async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field parameter#1 isn't displayed")
await fields[0].sendKeys(address)
})
it("Button 'Call data' is displayed and enabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[1], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[1])
assert.equal(text.toLowerCase(), address.toLowerCase(), 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), address.toLowerCase(), "output wasn't copied to clipboard")
})
it("2nd call doesn't throw the error", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
await button.click()
const field = await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
assert.notEqual(field, false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(field)
assert.equal(text.toLowerCase(), address.toLowerCase(), 'incorrect value was returned')
})
})
describe('Check output for data type : STRING', () => {
const stringValue = 'POA network'
it("Select method 'returnString'", async () => {
await f.delay(3000)
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[14].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it('Fill out input parameter field ', async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field parameter#1 isn't displayed")
await fields[0].sendKeys(stringValue)
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[1], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[1])
assert.equal(text, stringValue, 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), stringValue.toLowerCase(), "output wasn't copied to clipboard")
})
})
describe('Check output for data type : BOOLEAN', () => {
it("Select method 'returnBoolean'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[5].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it('Select value TRUE from dropdown menu', async () => {
const arrows = await f.driver.findElements(screens.executeMethod.selectArrow)
await arrows[1].click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
assert.equal(await list[1].getText(), 'true', 'TRUE menu item: incorrect text')
assert.equal(list.length, 2, "drop down menu isn't displayed")
await list[1].click()
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value: TRUE', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[0], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[0])
assert.equal(text, 'true', 'incorrect value was returned')
})
it('Select value FALSE from dropdown menu', async () => {
const arrows = await f.driver.findElements(screens.executeMethod.selectArrow)
await arrows[1].click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
assert.equal(await list[0].getText(), 'false', 'FALSE menu item: incorrect text')
assert.equal(list.length, 2, "drop down menu isn't displayed")
await list[0].click()
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value, FALSE', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[0], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[0])
assert.equal(text, 'false', 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), 'false', "output wasn't copied to clipboard")
})
})
describe('Check output for data type : BYTES', () => {
const bytesValue = '0x010203'
it("Select method 'returnBytes1'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[7].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it('Fill out input parameter field ', async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field parameter#1 isn't displayed")
await fields[0].sendKeys(bytesValue)
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[1], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[1])
assert.equal(text, bytesValue, 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), bytesValue.toLowerCase(), "output wasn't copied to clipboard")
})
})
describe('Check output for data type : UINT256', () => {
const uint256Value = '1122334455667788991122334455667788'
it("Select method 'returnUint256'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[17].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it('Fill out input parameter field ', async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field parameter#1 isn't displayed")
await fields[0].sendKeys(uint256Value)
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[1], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[1])
assert.equal(text, uint256Value, 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), uint256Value.toLowerCase(), "output wasn't copied to clipboard")
})
})
describe('Check output for data type : INT256', () => {
const int256Value = '-1122334455667788991122334455667788'
it("Select method 'returnInt256'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[10].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it('Fill out input parameter field ', async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field parameter#1 isn't displayed")
await fields[0].sendKeys(int256Value)
})
it("Click button 'Call data' ", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCall)
assert.notEqual(button, false, "button 'Call data' isn't displayed")
assert.equal(await button.isEnabled(), true, "Button 'Call data' is disabled")
await button.click()
})
it('method returns correct value', async () => {
await f.delay(3000)
await f.waitUntilShowUp(screens.executeMethod.fieldOutput)
const fields = await f.driver.findElements(screens.executeMethod.fieldOutput)
assert.notEqual(fields[1], false, "field 'Output' isn't displayed")
const text = await f.waitUntilHasValue(fields[1])
assert.equal(text, int256Value, 'incorrect value was returned')
})
it('icon copy cliboard is displayed and clickable', async () => {
const icon = await f.waitUntilShowUp(screens.executeMethod.copy)
assert.notEqual(icon, false, 'icon copy isn\'t displayed')
await icon.click()
})
it('Check clipboard buffer', async () => {
const text = clipboardy.readSync()
assert.equal(text.toLowerCase(), int256Value.toLowerCase(), "output wasn't copied to clipboard")
})
})
describe('Check executed method', () => {
it("Select method 'transfer'", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.selectArrow)
await field.click()
await f.waitUntilShowUp(screens.executeMethod.items)
const list = await f.driver.findElements(screens.executeMethod.items)
await list[21].click()
assert.equal(list.length, 22, "drop down menu isn't displayed")
})
it("Button 'Copy ABI encoded' is displayed", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCopyABI)
assert.notEqual(button, false, "button 'Copy ABI encoded' isn't displayed")
})
it("Button 'Copy ABI encoded' is disabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCopyABI)
assert.equal(await button.isEnabled(), false, "button 'Copy ABI encoded' enabled")
})
it("Button 'Next' is disabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
assert.equal(await button.isEnabled(), false, "button 'Next' enabled")
})
it("Fill out parameter '_value' with valid data", async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[1], false, "field address isn't displayed")
await fields[1].sendKeys('1')
})
it("Button 'Copy ABI encoded' is disabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCopyABI)
assert.equal(await button.isEnabled(), false, "button 'Copy ABI encoded' enabled")
})
it("Button 'Next' is disabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
assert.equal(await button.isEnabled(), false, "button 'Next' enabled")
})
it("Fill out parameter '_to' with wrong data", async () => {
await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
const fields = await f.driver.findElements(screens.executeMethod.fieldParameter)
assert.notEqual(fields[0], false, "field address isn't displayed")
await fields[0].sendKeys(wrongAddress)
})
it("Error message if click 'Copy ABI encoded' with wrong address", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCopyABI)
await button.click()
const error = await f.waitUntilShowUp(elements.error)
assert.notEqual(error, false, 'no error message')
})
it('Close error message', async () => {
const button = await f.waitUntilShowUp(elements.errorClose)
await button.click()
const title = await f.waitUntilShowUp(screens.executeMethod.title)
assert.notEqual(title, false, "error message isn't closed")
})
it.skip("Error message if click 'Next' with wrong address", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
await button.click()
const error = await f.waitUntilShowUp(elements.error)
assert.notEqual(error, false, 'no error message')
})
it.skip('Close error message', async () => {
const button = await f.waitUntilShowUp(elements.errorClose)
await button.click()
const title = await f.waitUntilShowUp(screens.executeMethod.title)
assert.notEqual(title, false, "error message isn't closed")
})
it("Fill out parameter '_to' with valid data", async () => {
const field = await f.waitUntilShowUp(screens.executeMethod.fieldParameter)
await f.clearField(field, 100)
await field.sendKeys(notContractAddress)
assert.notEqual(field, false, "field address isn't displayed")
})
it("Button 'Next' is enabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
assert.equal(await button.isEnabled(), true, "button 'Next' disabled")
})
it("Button 'Copy ABI encoded' is enabled", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonCopyABI)
assert.equal(await button.isEnabled(), true, "button 'Copy ABI encoded' disabled")
await button.click()
})
it("Click button 'Next'", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
assert.notEqual(button, false, "button 'Next' isn't displayed")
await button.click()
})
})
})
describe('Choose Contract Executor', () => {
it('Title is displayed and correct', async () => {
await f.delay(5000)
const title = await f.waitUntilShowUp(screens.chooseContractExecutor.title)
assert.notEqual(title, false, 'title isn\'t displayed')
assert.equal(await title.getText(), screens.chooseContractExecutor.titleText, 'incorrect text')
})
it('Two accounts displayed', async () => {
const accs = await f.waitUntilShowUp(screens.chooseContractExecutor.account)
assert.notEqual(accs, false, 'accounts aren\'t displayed')
const accounts = await f.driver.findElements(screens.chooseContractExecutor.account)
assert.equal(accounts.length, 4, "number of accounts isn't 2")
})
it("Click arrow button leads to 'Execute Method' screen ", async () => {
const button = await f.waitUntilShowUp(screens.chooseContractExecutor.buttonArrow)
assert.notEqual(button, false, 'button isn\'t displayed')
await button.click()
await f.delay(2000)
const title = await f.waitUntilShowUp(screens.executeMethod.title)
assert.notEqual(title, false, 'title isn\'t displayed')
assert.equal(await title.getText(), screens.executeMethod.titleText, "'Execute Method' screen isn't opened")
})
it("Return back to 'Choose Contract Executor' screen", async () => {
const button = await f.waitUntilShowUp(screens.executeMethod.buttonNext)
assert.notEqual(button, false, "button 'Next' isn't displayed")
await button.click()
})
it("Button 'Next' is disabled by default", async () => {
const button = await f.waitUntilShowUp(screens.chooseContractExecutor.buttonNext)
assert.notEqual(button, false, 'button isn\'t displayed')
assert.equal(await button.isEnabled(), false, 'button enabled by default')
})
it('User is able to select account', async () => {
await f.waitUntilShowUp(screens.chooseContractExecutor.account)
const accounts = await f.driver.findElements(screens.chooseContractExecutor.account)
const account = accounts[1]
await account.click()
const selected = await f.driver.findElements(screens.chooseContractExecutor.selectedAccount)
assert.equal(selected.length, 1, "account isn't selected")
})
it('User is able to select only one account', async () => {
const account = (await f.driver.findElements(screens.chooseContractExecutor.account))[2]
await account.click()
const selected = await f.driver.findElements(screens.chooseContractExecutor.selectedAccount)
assert.equal(selected.length, 1, 'more than one accounts are selected')
})
it("Click button 'Next' open 'Confirm transaction' screen", async () => {
const button = await f.waitUntilShowUp(screens.chooseContractExecutor.buttonNext)
await button.click()
await f.delay(3000)
const reject = await f.waitUntilShowUp(screens.confirmTransaction.button.reject)
assert.notEqual(reject, false, "button reject isn't displayed")
})
it("Button 'Buy Ether' is displayed", async () => {
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.buyEther)
assert.equal(await button.getText(), 'Buy Ether', 'button has incorrect name')
assert.equal(await button.isEnabled(), true, 'button is disabled')
})
it("Open screen 'Buy Ether'", async () => {
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.buyEther)
await button.click()
const title = await f.waitUntilShowUp(screens.buyEther.title)
assert.equal(await title.getText(), 'Buy POA', "screen 'Buy Ether' has incorrect title text")
const arrow = await f.waitUntilShowUp(elements.buttonArrow)
await arrow.click()
})
it("Click button 'Reject' open contract's account screen", async () => {
const reject = await f.waitUntilShowUp(screens.confirmTransaction.button.reject)
assert.equal(await reject.getText(), 'Reject', 'button has incorrect name')
await reject.click()
const buttonExecute = await f.waitUntilShowUp(screens.executeMethod.buttonExecuteMethod)
assert.notEqual(buttonExecute, false, "contract's account hasn't opened")
})
it("Button arrow leads to executor's account screen", async () => {
assert.equal(await f.executeTransferMethod(0, account1), true, "can't execute the method 'transfer'")
await f.delay(2000)
const arrow = await f.waitUntilShowUp(elements.buttonArrow)
await arrow.click()
await f.delay(2000)
const address = await f.waitUntilShowUp(screens.main.address)
assert.equal((await address.getText()).toUpperCase(), getCreatedAccounts()[0], "executors account isn't opened")
})
it('Switch to contract account ', async () => {
const accountMenu = await f.waitUntilShowUp(menus.account.menu)
await accountMenu.click()
const item = await f.waitUntilShowUp(menus.account.account4)
await item.click()
await f.delay(2000)
const address = await f.waitUntilShowUp(screens.main.address)
assert.equal((await address.getText()).toUpperCase(), contractSokol.toUpperCase(), "contract's account isn't opened")
})
it("Confirm transaction: button 'Reject All' leads to contract's account screen", async () => {
assert.equal(await f.executeTransferMethod(0, account1), true, "can't execute the method 'transfer'")
const rejectAll = await f.waitUntilShowUp(screens.confirmTransaction.button.rejectAll)
assert.equal(await rejectAll.getText(), 'Reject All', 'button has incorrect name')
await rejectAll.click()
await f.delay(2000)
const address = await f.waitUntilShowUp(screens.main.address)
assert.equal((await address.getText()).toUpperCase(), contractSokol.toUpperCase(), "contract account isn't opened")
})
it("Confirm transaction: button 'Submit' leads to contract's account screen", async () => {
assert.equal(await f.executeTransferMethod(2, account1), true, "can't execute the method 'transfer'")
await f.delay(2000)
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.submit)
assert.equal(await button.getAttribute('value'), 'Submit', 'button has incorrect name')
await button.click()
await f.delay(2000)
const address = await f.waitUntilShowUp(screens.main.address)
assert.equal((await address.getText()).toUpperCase(), contractSokol.toUpperCase(), "contract account isn't opened")
})
it("Label 'CONTRACT' present", async () => {
const menu = await f.waitUntilShowUp(menus.account.menu)
await menu.click()
await f.waitUntilShowUp(menus.account.label)
const label = (await f.driver.findElements(menus.account.label))[1]
assert.equal(await label.getText(), 'CONTRACT', 'label incorrect')
})
it('Delete imported account', async () => {
await f.waitUntilShowUp(menus.account.delete)
const items = await f.driver.findElements(menus.account.delete)
await items[1].click()
const button = await f.waitUntilShowUp(screens.deleteImportedAccount.buttons.yes)
await button.click()
const buttonArrow = await f.waitUntilShowUp(screens.settings.buttons.arrow)
await buttonArrow.click()
const identicon = await f.waitUntilShowUp(screens.main.identicon)
assert.notEqual(identicon, false, 'main screen didn\'t opened')
})
})
})
}
module.exports = importContractAccount

View File

@ -0,0 +1,68 @@
const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements')
const testSeedPhrase = 'horn among position unable audit puzzle cannon apology gun autumn plug parrot'
const importGanacheSeedPhrase = async (f, account2, password) => {
it('logs out', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
const menu = await f.waitUntilShowUp(menus.sandwich.menu)
await menu.click()
const logOut = await f.waitUntilShowUp(menus.sandwich.logOut)
assert.equal(await logOut.getText(), menus.sandwich.textLogOut)
await logOut.click()
})
it('restores from seed phrase', async () => {
const restoreSeedLink = await f.waitUntilShowUp(screens.lock.linkRestore)
assert.equal(await restoreSeedLink.getText(), screens.lock.linkRestoreText)
await restoreSeedLink.click()
})
it('adds seed phrase', async () => {
const seedTextArea = await f.waitUntilShowUp(screens.restoreVault.textArea)
await seedTextArea.sendKeys(testSeedPhrase)
let field = await f.driver.findElement(screens.restoreVault.fieldPassword)
await field.sendKeys(password)
field = await f.driver.findElement(screens.restoreVault.fieldPasswordConfirm)
await field.sendKeys(password)
field = await f.waitUntilShowUp(screens.restoreVault.buttos.ok)
await f.click(field)
})
it('balance renders', async () => {
const balance = await f.waitUntilShowUp(screens.main.balance)
assert.equal(await balance.getText(), '100.000', "balance isn't correct")
})
it('sends transaction', async () => {
const sendButton = await f.waitUntilShowUp(screens.main.buttons.send)
assert.equal(await sendButton.getText(), screens.main.buttons.sendText)
await f.click(sendButton)
})
it('adds recipient address and amount', async () => {
const sendTranscationScreen = await f.waitUntilShowUp(screens.sendTransaction.title)
assert.equal(await sendTranscationScreen.getText(), screens.sendTransaction.titleText, 'Transaction screen has incorrect titlr')
const inputAddress = await f.waitUntilShowUp(screens.sendTransaction.field.address)
const inputAmmount = await f.waitUntilShowUp(screens.sendTransaction.field.amount)
await inputAddress.sendKeys(account2)
await inputAmmount.sendKeys('10')
const button = await f.waitUntilShowUp(screens.sendTransaction.buttonNext)
assert.equal(await button.getText(), 'Next', 'button has incorrect name')
await f.click(button)
})
it('confirms transaction', async () => {
const button = await f.waitUntilShowUp(screens.confirmTransaction.button.submit)
assert.equal(await button.getAttribute('value'), 'Submit', 'button has incorrect name')
await f.click(button)
})
it('finds the transaction in the transactions list', async () => {
const transactionAmount = await f.waitUntilShowUp(screens.main.transactionList)
assert.equal(await transactionAmount.getText(), '10.0')
})
}
module.exports = importGanacheSeedPhrase

View File

@ -0,0 +1,58 @@
const assert = require('assert')
const { screens } = require('../elements')
const login = async (f, password) => {
it('title is \'Nifty Wallet\'', async () => {
const title = await f.driver.getTitle()
assert.equal(title, 'Nifty Wallet', 'title is incorrect')
})
it('screen \'Terms of Use\' has not empty agreement', async () => {
await f.delay(5000)
const terms = await f.waitUntilShowUp(screens.TOU.agreement, 900)
const text = await terms.getText()
assert.equal(text.length > 400, true, 'agreement is too short')
})
it('screen \'Terms of Use\' has correct title', async () => {
const terms = await f.waitUntilShowUp(screens.TOU.title)
assert.equal(await terms.getText(), screens.TOU.titleText, 'title is incorrect')
})
it('checks if the TOU contains link \'Terms of service\'', async () => {
const element = await f.waitUntilShowUp(screens.TOU.linkTerms)
await f.scrollTo(screens.TOU.linkTerms)
assert.notEqual(element, null, ' link \'Terms of service\' isn\'t present')
assert.equal(await element.getText(), screens.TOU.linkTermsText, 'incorrect name of link \'Terms of service\'')
})
it('checks if the button \'Accept\' is present and enabled', async () => {
const button = await f.waitUntilShowUp(screens.TOU.button)
assert.notEqual(button, false, 'button isn\'t present')
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
assert.equal(await button.getText(), 'Accept', 'button has incorrect name')
await f.click(button)
})
it('accepts password with length of eight', async () => {
const passwordBox = await f.waitUntilShowUp(screens.create.fieldPassword)
const passwordBoxConfirm = await f.waitUntilShowUp(screens.create.fieldPasswordConfirm)
const button = await f.waitUntilShowUp(screens.create.button)
assert.equal(await button.getText(), 'Create', 'button has incorrect name')
await passwordBox.sendKeys(password)
await passwordBoxConfirm.sendKeys(password)
await f.click(button)
})
it('shows vault was created and seed phrase', async () => {
await f.delay(300)
const element = await f.waitUntilShowUp(screens.seedPhrase.fieldPhrase)
const seedPhrase = await element.getText()
assert.equal(seedPhrase.split(' ').length, 12)
const continueAfterSeedPhrase = await f.waitUntilShowUp(screens.seedPhrase.buttonIveCopied)
assert.equal(await continueAfterSeedPhrase.getText(), screens.seedPhrase.textButtonIveCopied)
await f.click(continueAfterSeedPhrase)
})
}
module.exports = login

View File

@ -0,0 +1,9 @@
const setup = async (f) => {
it('switches to extensions list', async () => {
await f.delay(300)
await f.switchToFirstPage()
await f.delay(5000)
})
}
module.exports = setup

View File

@ -0,0 +1,67 @@
const assert = require('assert')
const webdriver = require('selenium-webdriver')
const { By } = webdriver
const { screens, NETWORKS } = require('../elements')
const signData = async (f) => {
it('Simulate sign request ', async () => {
await f.delay(5000)
await f.setProvider(NETWORKS.LOCALHOST)
await f.driver.get('https://danfinlay.github.io/js-eth-personal-sign-examples/')
const button = await f.waitUntilShowUp(By.id('ethSignButton'))
assert.notEqual(button, false, "resource isn't responding")
await button.click()
await f.delay(5000)
})
it('navigates back to MetaMask popup in the tab', async () => {
if (process.env.SELENIUM_BROWSER === 'chrome') {
await f.driver.get(`chrome-extension://${f.extensionId}/popup.html`)
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
await f.driver.get(`moz-extension://${f.extensionId}/popup.html`)
}
await f.delay(700)
})
it('error message is displayed and contains text', async () => {
const error = await f.waitUntilShowUp(screens.signMessage.error)
assert.notEqual(error, false, 'error message isn\'t displayed')
const text = await error.getText()
assert.equal(text.length > 183, true, 'error message hasn\'t text')
})
it('account name is displayed and correct', async () => {
const name = await f.waitUntilShowUp(screens.signMessage.accountName)
assert.notEqual(name, false, 'account name isn\'t displayed')
assert.equal(await name.getText(), 'new name', 'account name is incorrect')
})
it('title is displayed and correct', async () => {
const title = await f.waitUntilShowUp(screens.signMessage.title)
assert.notEqual(title, false, 'title isn\'t displayed')
assert.equal(await title.getText(), 'Sign message', 'title is incorrect')
})
it('message is displayed and correct', async () => {
const message = await f.waitUntilShowUp(screens.signMessage.message)
assert.notEqual(message, false, 'message isn\'t displayed')
assert.equal((await message.getText()).length > 32, true, 'message is incorrect')
})
it('button \'Cancel\' is enabled and lead to main screen ', async () => {
const button = await f.waitUntilShowUp(screens.signMessage.buttons.cancel)
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
assert.equal(await button.getText(), 'Cancel', 'button has incorrect name')
})
it('button \'Sign\' is enabled and lead to main screen ', async () => {
const button = await f.waitUntilShowUp(screens.signMessage.buttons.sign)
assert.equal(await button.isEnabled(), true, 'button isn\'t enabled')
assert.equal(await button.getText(), 'Sign', 'button has incorrect name')
await f.click(button)
const identicon = await f.waitUntilShowUp(screens.main.identicon)
assert.notEqual(identicon, false, 'main screen didn\'t opened')
})
}
module.exports = signData

View File

@ -0,0 +1,54 @@
const assert = require('assert')
const {
nestedJsonObjToArray,
} = require('../../../../../../old-ui/app/accounts/import/helpers')
describe('#nestedJsonObjToArray', () => {
const JsonPattern1 = {
key1: 'val1',
key2: 'val2',
}
const JsonPattern2 = {
key1: 'val1',
key2: {
key3_1: 'val2',
key3_2: 'val3',
},
}
const JsonPattern3 = {
key1: 'val1',
key2: ['val2', 'val3'],
key3: {
key3_1: 'val4',
key3_2: 'val5',
},
}
const JsonPattern4 = {
key1: 'val1',
key2: {
key3_1: 'val2',
key3_2: {
key3_2_1: 'val3',
key3_2_3: 'val4',
},
},
}
const JsonPattern5 = {
key1: 'val1',
key2: {
key3_1: 'val2',
key3_2: {
key3_2_1: 'val3',
key3_2_3: ['val4', 'val5', {key3_2_3_1: 'val6'}],
},
},
}
it('converts nested json objects to arrays correctly', () => {
assert.deepEqual(['val1', 'val2'], nestedJsonObjToArray(JsonPattern1))
assert.deepEqual(['val1', 'val2', 'val3'], nestedJsonObjToArray(JsonPattern2))
assert.deepEqual(['val1', 'val2', 'val3', 'val4', 'val5'], nestedJsonObjToArray(JsonPattern3))
assert.deepEqual(['val1', 'val2', 'val3', 'val4'], nestedJsonObjToArray(JsonPattern4))
assert.deepEqual(['val1', 'val2', 'val3', 'val4', 'val5', 'val6'], nestedJsonObjToArray(JsonPattern5))
})
})

View File

@ -103,6 +103,7 @@ var actions = {
changePassword,
getContract,
removeAccount,
updateABI,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN',
@ -662,6 +663,26 @@ function removeAccount (address, network) {
}
}
function updateABI (address, network, newABI) {
return dispatch => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
background.updateABI(address, network, newABI, (err, account) => {
dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
log.info('Implementation ABI for proxy updated: ' + account)
dispatch(actions.showAccountsPage())
resolve()
})
})
}
}
function addNewKeyring (type, opts) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())