Merge pull request #332 from poanetwork/vb-fix-remove-acc
Return to main screen after removal of imported account
This commit is contained in:
commit
fd1e6c9c61
4
.babelrc
4
.babelrc
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"presets": [["env", { "targets": { "browsers": [">0.25%", "not ie 11", "not op_mini all"] } } ], "react", "stage-0"],
|
||||
"plugins": ["transform-runtime", "transform-async-to-generator", "transform-class-properties"]
|
||||
}
|
|
@ -337,12 +337,6 @@ jobs:
|
|||
- run:
|
||||
name: sentry sourcemaps upload
|
||||
command: npm run sentry:publish
|
||||
# - run:
|
||||
# name: github gh-pages docs publish
|
||||
# command: >
|
||||
# git config user.name metamaskbot
|
||||
# git config user.email admin@metamask.io
|
||||
# gh-pages -d docs/jsdocs
|
||||
|
||||
test-unit:
|
||||
docker:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## Current Master
|
||||
|
||||
- [#332](https://github.com/poanetwork/nifty-wallet/pull/332) - (Chore) Return to main screen from removal of imported account
|
||||
- [#330](https://github.com/poanetwork/nifty-wallet/pull/330) - (Fix) Derive correct addresses for custom networks (RSK/ETC)
|
||||
- [#329](https://github.com/poanetwork/nifty-wallet/pull/329) - (Fix) Connect to unknown private network fix
|
||||
- [#326](https://github.com/poanetwork/nifty-wallet/pull/326) - (Chore) HTTP2 RPC endpoints for POA and xDai
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built.
|
||||
|
||||
## Contributing
|
||||
|
||||
You can re-generate the docs locally by running `npm run doc`, and contributors can update the hosted docs by running `npm run publish-docs`.
|
||||
|
||||
### Running Tests
|
||||
|
||||
Requires `mocha` installed. Run `npm install -g mocha`.
|
||||
|
|
|
@ -110,7 +110,7 @@ class ExtensionPlatform {
|
|||
})
|
||||
}
|
||||
|
||||
_subscribeToNotificationClicked = () => {
|
||||
_subscribeToNotificationClicked () {
|
||||
if (extension.notifications.onClicked.hasListener(this._viewOnExplorer)) {
|
||||
extension.notifications.onClicked.removeListener(this._viewOnExplorer)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
module.exports = function (api) {
|
||||
api.cache(false)
|
||||
return {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
browsers: [
|
||||
'chrome >= 58',
|
||||
'firefox >= 56.2',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
],
|
||||
}
|
||||
}
|
|
@ -14,12 +14,12 @@ To use this class, simply take your form component (the component that renders `
|
|||
|
||||
You can see an example of this in use in `ui/app/first-time/restore-vault.js`.
|
||||
|
||||
Additionally, any field whose value should be persisted, should have a `persistentFormId` attribute, which needs to be assigned under a `dataset` key on the main `attributes` hash. For example:
|
||||
Additionally, any field whose value should be persisted, should have a `persistentFormid` attribute, which needs to be assigned under a `dataset` key on the main `attributes` hash. For example:
|
||||
|
||||
```javascript
|
||||
return h('textarea.twelve-word-phrase.letter-spacey', {
|
||||
dataset: {
|
||||
persistentFormId: 'wallet-seed',
|
||||
persistentFormid: 'wallet-seed',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
|
|
@ -5,7 +5,19 @@ import classnames from 'classnames'
|
|||
import LoadingScreen from './loading-screen'
|
||||
import {importNewAccount, hideWarning} from '../../../../ui/app/actions'
|
||||
|
||||
const Input = ({ label, placeholder, onChange, errorMessage, type = 'text' }) => (
|
||||
class Input extends Component {
|
||||
|
||||
static propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render () {
|
||||
const {label, type, placeholder, errorMessage, onChange} = this.props
|
||||
return (
|
||||
<div className="import-account__input-wrapper">
|
||||
<div className="import-account__input-label">{label}</div>
|
||||
<input
|
||||
|
@ -19,14 +31,8 @@ const Input = ({ label, placeholder, onChange, errorMessage, type = 'text' }) =>
|
|||
<div className="import-account__input-error-message">{errorMessage}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Input.prototype.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
}
|
||||
|
||||
class ImportAccountScreen extends Component {
|
||||
static OPTIONS = {
|
||||
|
|
|
@ -6,8 +6,6 @@ When you log in to Nifty Wallet, your current account's address is visible to ev
|
|||
|
||||
For your privacy, for now, please sign out of Nifty Wallet when you're done using a site.
|
||||
|
||||
There have been several instances of high-profile legitimate websites such as BTC Manager and Games Workshop that have had their websites temporarily compromised. This involves showing a fake Nifty Wallet window on the page asking for user's seed phrases. Nifty Wallet will never open itself in this way.
|
||||
|
||||
## Related Links ##
|
||||
|
||||
**[Terms of Service](https://github.com/poanetwork/metamask-extension/wiki/Terms-of-Service)**
|
|
@ -54,7 +54,7 @@ class AccountsDropdownItemView extends Component {
|
|||
onClick={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.props.actions.showDeleteImportedAccount(identity)
|
||||
this.props.actions.showDeleteImportedAccount(identity, keyring)
|
||||
this.props.closeMenu()
|
||||
}}
|
||||
/>) : null
|
||||
|
@ -157,7 +157,7 @@ const mapDispatchToProps = (dispatch) => {
|
|||
return {
|
||||
actions: {
|
||||
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
|
||||
showDeleteImportedAccount: (identity) => dispatch(actions.showDeleteImportedAccount(identity)),
|
||||
showDeleteImportedAccount: (identity, keyring) => dispatch(actions.showDeleteImportedAccount(identity, keyring)),
|
||||
getContract: (addr) => dispatch(actions.getContract(addr)),
|
||||
connectHardwareAndUnlockAddress: (deviceName, hdPath, address) => {
|
||||
return dispatch(actions.connectHardwareAndUnlockAddress(deviceName, hdPath, address))
|
||||
|
|
|
@ -39,6 +39,7 @@ export default class AddTokenScreen extends Component {
|
|||
address: PropTypes.string,
|
||||
dispatch: PropTypes.func,
|
||||
network: PropTypes.string,
|
||||
addToken: PropTypes.func,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
|
|
|
@ -17,6 +17,8 @@ class NetworksMenu extends Component {
|
|||
provider: PropTypes.any.isRequired,
|
||||
frequentRpcList: PropTypes.array.isRequired,
|
||||
isNetworkMenuOpen: PropTypes.bool,
|
||||
setProviderType: PropTypes.function,
|
||||
showDeleteRPC: PropTypes.function,
|
||||
}
|
||||
|
||||
render () {
|
||||
|
|
|
@ -14,6 +14,11 @@ import PropTypes from 'prop-types'
|
|||
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
|
||||
|
||||
class BuyButtonSubview extends Component {
|
||||
static propTypes = {
|
||||
isSubLoading: PropTypes.bool,
|
||||
identity: PropTypes.object,
|
||||
account: PropTypes.object,
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
|
|
|
@ -2,34 +2,58 @@ import ConfirmScreen from './confirm'
|
|||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import actions from '../../../ui/app/actions'
|
||||
import { ifContractAcc } from '../util'
|
||||
|
||||
class DeleteImportedAccount extends ConfirmScreen {
|
||||
static propTypes = {
|
||||
}
|
||||
|
||||
render () {
|
||||
const withDescription = !ifContractAcc(this.props.keyring)
|
||||
return (
|
||||
<ConfirmScreen
|
||||
subtitle="Delete Imported Account"
|
||||
withDescription={true}
|
||||
withDescription={withDescription}
|
||||
description="Be sure, that you saved a private key or JSON keystore file of this account in a safe place. Otherwise, you will not be able to restore this account."
|
||||
question={`Are you sure to delete imported ${this.props.identity.name} (${this.props.identity.address})?`}
|
||||
onCancelClick={() => this.props.dispatch(actions.showConfigPage())}
|
||||
onNoClick={() => this.props.dispatch(actions.showConfigPage())}
|
||||
onYesClick={() => {
|
||||
this.props.dispatch(actions.removeAccount(this.props.identity.address, this.props.metamask.network))
|
||||
.then(() => {
|
||||
this.props.dispatch(actions.showConfigPage())
|
||||
})
|
||||
}}
|
||||
onCancelClick={() => this.onCancelClick()}
|
||||
onNoClick={() => this.onNoClick()}
|
||||
onYesClick={() => this.onYesClick()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
onCancelClick () {
|
||||
this.props.showAccountsPage()
|
||||
}
|
||||
|
||||
onNoClick () {
|
||||
this.props.showAccountsPage()
|
||||
}
|
||||
|
||||
onYesClick () {
|
||||
this.props.removeAccount(this.props.identity.address, this.props.metamask.network)
|
||||
.then(() => {
|
||||
this.props.showAccountsPage()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
metamask: state.metamask,
|
||||
identity: state.appState.identity,
|
||||
keyring: state.appState.keyring,
|
||||
provider: state.metamask.provider,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(DeleteImportedAccount)
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
removeAccount: (address, network) => dispatch(actions.removeAccount(address, network)),
|
||||
showAccountsPage: () => dispatch(actions.showAccountsPage()),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(DeleteImportedAccount)
|
||||
|
|
|
@ -4,6 +4,11 @@ import PropTypes from 'prop-types'
|
|||
import { DAI_CODE, POA_SOKOL_CODE, RSK_TESTNET_CODE, GOERLI_TESTNET_CODE } from '../../../app/scripts/controllers/network/enums'
|
||||
|
||||
class FiatValue extends Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.number,
|
||||
valueStyle: PropTypes.object,
|
||||
dimStyle: PropTypes.object,
|
||||
}
|
||||
render = () => {
|
||||
const props = this.props
|
||||
let { conversionRate } = props
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import React, {Component} from 'react'
|
||||
import AccountPanel from './account-panel'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsgDetails extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
selectedAddress: PropTypes.string,
|
||||
identities: PropTypes.object,
|
||||
accounts: PropTypes.array,
|
||||
imageifyIdenticons: PropTypes.any,
|
||||
}
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import React, { Component } from 'react'
|
||||
import PendingTxDetails from './pending-msg-details'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsg extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
cancelMessage: PropTypes.function,
|
||||
signMessage: PropTypes.function,
|
||||
}
|
||||
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
import React, {Component} from 'react'
|
||||
import AccountPanel from './account-panel'
|
||||
import BinaryRenderer from './binary-renderer'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsgDetails extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
selectedAddress: PropTypes.string,
|
||||
identities: PropTypes.object,
|
||||
accounts: PropTypes.object,
|
||||
imageifyIdenticons: PropTypes.object,
|
||||
}
|
||||
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import React, { Component } from 'react'
|
||||
import PendingTxDetails from './pending-personal-msg-details'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsg extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
cancelPersonalMessage: PropTypes.function,
|
||||
signPersonalMessage: PropTypes.function,
|
||||
}
|
||||
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
import React, {Component} from 'react'
|
||||
import AccountPanel from './account-panel'
|
||||
import TypedMessageRenderer from './typed-message-renderer'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsgDetails extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
selectedAddress: PropTypes.string,
|
||||
identities: PropTypes.object,
|
||||
accounts: PropTypes.array,
|
||||
imageifyIdenticons: PropTypes.any,
|
||||
}
|
||||
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import React, { Component } from 'react'
|
||||
import PendingTxDetails from './pending-typed-msg-details'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PendingMsg extends Component {
|
||||
static propTypes = {
|
||||
txData: PropTypes.object,
|
||||
cancelTypedMessage: PropTypes.function,
|
||||
signTypedMessage: PropTypes.function,
|
||||
}
|
||||
|
||||
render () {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
|
|
@ -8,11 +8,10 @@ import ErrorComponent from '../error'
|
|||
import ToastComponent from '../toast'
|
||||
import Select from 'react-select'
|
||||
import actions from '../../../../ui/app/actions'
|
||||
import { AbiCoder } from 'web3-eth-abi'
|
||||
import abi from 'web3-eth-abi'
|
||||
import Web3 from 'web3'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import CopyButton from '../copy/copy-button'
|
||||
const abiEncoder = new AbiCoder()
|
||||
|
||||
class SendTransactionField extends Component {
|
||||
constructor (props) {
|
||||
|
@ -430,7 +429,7 @@ class SendTransactionScreen extends PersistentForm {
|
|||
const inputValuesArray = Object.keys(inputValues).map(key => inputValues[key])
|
||||
let txData
|
||||
try {
|
||||
txData = abiEncoder.encodeFunctionCall(methodABI, inputValuesArray)
|
||||
txData = abi.encodeFunctionCall(methodABI, inputValuesArray)
|
||||
this.props.hideWarning()
|
||||
} catch (e) {
|
||||
this.props.hideToast()
|
||||
|
|
|
@ -5,8 +5,20 @@ import { addressSummary } from '../../util'
|
|||
import EthBalance from '../eth-balance'
|
||||
import TokenBalance from '../token-balance'
|
||||
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class SendProfile extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string,
|
||||
account: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
network: PropTypes.string,
|
||||
conversionRate: PropTypes.number,
|
||||
currentCurrency: PropTypes.string,
|
||||
isToken: PropTypes.bool,
|
||||
token: PropTypes.any,
|
||||
}
|
||||
|
||||
render () {
|
||||
const props = this.props
|
||||
const {
|
||||
|
|
|
@ -103,7 +103,7 @@ SendTransactionScreen.prototype.render = function () {
|
|||
marginRight: '6px',
|
||||
},
|
||||
dataset: {
|
||||
persistentFormId: 'tx-amount',
|
||||
persistentFormid: 'tx-amount',
|
||||
},
|
||||
}),
|
||||
|
||||
|
@ -137,7 +137,7 @@ SendTransactionScreen.prototype.render = function () {
|
|||
resize: 'none',
|
||||
},
|
||||
dataset: {
|
||||
persistentFormId: 'tx-data',
|
||||
persistentFormid: 'tx-data',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
|
|
@ -63,7 +63,7 @@ ShapeshiftForm.prototype.renderMain = function () {
|
|||
list: 'coinList',
|
||||
autoFocus: true,
|
||||
dataset: {
|
||||
persistentFormId: 'input-coin',
|
||||
persistentFormid: 'input-coin',
|
||||
},
|
||||
style: {
|
||||
boxSizing: 'border-box',
|
||||
|
@ -149,7 +149,7 @@ ShapeshiftForm.prototype.renderRefundAddressForCoin = function (coin) {
|
|||
type: 'text',
|
||||
placeholder: `Your ${coin} Refund Address`,
|
||||
dataset: {
|
||||
persistentFormId: 'refund-address',
|
||||
persistentFormid: 'refund-address',
|
||||
|
||||
},
|
||||
style: {
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import React, { Component } from 'react'
|
||||
import ReactTooltip from 'react-tooltip'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class Tooltip extends Component {
|
||||
|
||||
static propTypes = {
|
||||
position: PropTypes.any,
|
||||
title: PropTypes.string,
|
||||
id: PropTypes.any,
|
||||
children: PropTypes.any,
|
||||
}
|
||||
|
||||
render () {
|
||||
const props = this.props
|
||||
const { position, title, children, id } = props
|
||||
|
|
|
@ -38,6 +38,7 @@ class ConfigScreen extends Component {
|
|||
static propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
metamask: PropTypes.object,
|
||||
warning: PropTypes.string,
|
||||
}
|
||||
|
||||
render () {
|
||||
|
|
|
@ -70,7 +70,7 @@ RestoreVaultScreen.prototype.render = function () {
|
|||
id: 'password-box',
|
||||
placeholder: 'New Password (min 8 chars)',
|
||||
dataset: {
|
||||
persistentFormId: 'password',
|
||||
persistentFormid: 'password',
|
||||
},
|
||||
style: {
|
||||
width: '100%',
|
||||
|
@ -86,7 +86,7 @@ RestoreVaultScreen.prototype.render = function () {
|
|||
placeholder: 'Confirm Password',
|
||||
onKeyPress: this.createOnEnter.bind(this),
|
||||
dataset: {
|
||||
persistentFormId: 'password-confirmation',
|
||||
persistentFormid: 'password-confirmation',
|
||||
},
|
||||
style: {
|
||||
width: '100%',
|
||||
|
|
File diff suppressed because it is too large
Load Diff
123
package.json
123
package.json
|
@ -8,7 +8,6 @@
|
|||
"mascara": "gulp dev:mascara & node ./mascara/example/server",
|
||||
"dist": "gulp dist",
|
||||
"doc": "jsdoc -c development/tools/.jsdoc.json",
|
||||
"publish-docs": "gh-pages -d docs/jsdocs",
|
||||
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
||||
"watch:test:unit": "nodemon --exec \"npm run test:unit\" ./test ./app ./ui",
|
||||
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\"",
|
||||
|
@ -58,43 +57,44 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"browsers": [
|
||||
">0.25%",
|
||||
"not ie 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
"targets": {
|
||||
"browsers": [
|
||||
"chrome >= 58",
|
||||
"firefox >= 56.2"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
"@babel/preset-react"
|
||||
]
|
||||
},
|
||||
{
|
||||
"plugins": [
|
||||
"transform-class-properties"
|
||||
"@babel/plugin-transform-runtime",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-proposal-optional-chaining"
|
||||
]
|
||||
}
|
||||
],
|
||||
"reactify",
|
||||
"brfs"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@material-ui/core": "^4.1.1",
|
||||
"@zxing/library": "^0.8.0",
|
||||
"abi-decoder": "^1.0.9",
|
||||
"abi-decoder": "^1.2.0",
|
||||
"asmcrypto.js": "0.22.0",
|
||||
"async": "^2.5.0",
|
||||
"await-semaphore": "^0.1.1",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bignumber.js": "^4.1.0",
|
||||
"bip39": "^2.2.0",
|
||||
"bluebird": "^3.5.0",
|
||||
"bn.js": "^4.11.7",
|
||||
"browser-passworder": "^2.0.3",
|
||||
"browserify-derequire": "^0.9.4",
|
||||
"browserify-unibabel": "^3.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"clone": "^2.1.1",
|
||||
|
@ -107,8 +107,7 @@
|
|||
"detectrtc": "^1.3.6",
|
||||
"disc": "^1.3.2",
|
||||
"dnode": "^1.2.2",
|
||||
"end-of-stream": "^1.1.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"end-of-stream": "^1.4.4",
|
||||
"eth-block-tracker": "^4.1.0",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#master",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
|
@ -137,10 +136,7 @@
|
|||
"extensionizer": "^1.0.1",
|
||||
"fast-json-patch": "^2.0.4",
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"file-loader": "^1.1.11",
|
||||
"fuse.js": "^3.2.0",
|
||||
"gulp": "github:gulpjs/gulp#v4.0.0",
|
||||
"gulp-autoprefixer": "^5.0.0",
|
||||
"human-standard-token-abi": "^2.0.0",
|
||||
"idb-global": "^2.1.0",
|
||||
"iframe-stream": "^3.0.0",
|
||||
|
@ -159,7 +155,7 @@
|
|||
"nifty-wallet-inpage-provider": "git+ssh://git@github.com/poanetwork/nifty-wallet-inpage-provider.git#1.2.3",
|
||||
"number-to-bn": "^1.7.0",
|
||||
"obj-multiplex": "^1.0.0",
|
||||
"obs-store": "^3.0.2",
|
||||
"obs-store": "^4.0.3",
|
||||
"percentile": "^1.2.0",
|
||||
"pify": "^3.0.0",
|
||||
"ping-pong-stream": "^1.0.0",
|
||||
|
@ -173,14 +169,13 @@
|
|||
"qrcode-npm": "0.0.3",
|
||||
"ramda": "^0.24.1",
|
||||
"raven-js": "^3.24.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-hyperscript": "^3.0.0",
|
||||
"react-inspector": "^2.3.0",
|
||||
"react-markdown": "^3.0.0",
|
||||
"react-media": "^1.8.0",
|
||||
"react-modal": "^3.8.1",
|
||||
"react-redux": "^7.1.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-select": "^1.0.0",
|
||||
"react-simple-file-input": "^2.0.0",
|
||||
|
@ -189,10 +184,9 @@
|
|||
"react-tooltip": "^3.10.0",
|
||||
"react-transition-group": "^1.2.1",
|
||||
"react-trigger-change": "^1.0.2",
|
||||
"reactify": "^1.1.1",
|
||||
"readable-stream": "^2.3.3",
|
||||
"recompose": "^0.25.0",
|
||||
"redux": "^3.0.5",
|
||||
"redux": "^4.0.5",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"request-promise": "^4.2.1",
|
||||
|
@ -207,31 +201,34 @@
|
|||
"url-parse": "^1.4.4",
|
||||
"valid-url": "^1.0.9",
|
||||
"vreme": "^3.0.2",
|
||||
"web3": "^0.20.1",
|
||||
"web3-eth-abi": "^1.0.0-beta.55",
|
||||
"web3-stream-provider": "^3.0.1",
|
||||
"web3": "^0.20.7",
|
||||
"web3-eth-abi": "^1.2.6",
|
||||
"web3-stream-provider": "^4.0.0",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/cli": "^1.30.3",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.5.5",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.5.5",
|
||||
"@babel/polyfill": "^7.6.0",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/register": "^7.5.5",
|
||||
"@sentry/cli": "^1.49.0",
|
||||
"@storybook/addon-info": "^5.3.14",
|
||||
"@storybook/addon-knobs": "^3.4.2",
|
||||
"@storybook/react": "^5.2.6",
|
||||
"@storybook/addon-knobs": "^5.3.14",
|
||||
"@storybook/react": "^5.3.14",
|
||||
"addons-linter": "^1.22.0",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-eslint": "^8.0.0",
|
||||
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-register": "^6.7.2",
|
||||
"babelify": "^8.0.0",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babelify": "^10.0.0",
|
||||
"brfs": "^1.6.1",
|
||||
"browserify": "^16.2.3",
|
||||
"browserify-derequire": "^1.0.1",
|
||||
"chai": "^4.1.0",
|
||||
"chromedriver": "^2.41.0",
|
||||
"chromedriver": "^79.0.0",
|
||||
"clipboardy": "^1.2.3",
|
||||
"compression": "^1.7.1",
|
||||
"coveralls": "^3.0.0",
|
||||
|
@ -239,37 +236,37 @@
|
|||
"css-loader": "^3.2.0",
|
||||
"deep-freeze-strict": "^1.1.1",
|
||||
"del": "^3.0.0",
|
||||
"envify": "^4.0.0",
|
||||
"enzyme": "^3.4.4",
|
||||
"enzyme-adapter-react-16": "^1.14.0",
|
||||
"eslint-plugin-chai": "0.0.1",
|
||||
"envify": "^4.1.0",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.15.1",
|
||||
"eslint": "^6.0.1",
|
||||
"eslint-plugin-babel": "^5.3.0",
|
||||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eslint-plugin-mocha": "^6.2.2",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"eth-json-rpc-middleware": "^3.1.3",
|
||||
"expect": "^25.0.0",
|
||||
"fetch-mock": "^6.5.2",
|
||||
"file-loader": "^1.1.11",
|
||||
"fs-extra": "^6.0.1",
|
||||
"fs-promise": "^2.0.3",
|
||||
"ganache-cli": "^6.7.0",
|
||||
"ganache-core": "^2.10.2",
|
||||
"geckodriver": "^1.19.1",
|
||||
"gh-pages": "^1.2.0",
|
||||
"gifencoder": "^1.1.0",
|
||||
"gulp": "github:gulpjs/gulp#v4.0.0",
|
||||
"gulp-babel": "^7.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^5.0.0",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-json-editor": "^2.2.1",
|
||||
"gulp-livereload": "^4.0.0",
|
||||
"gulp-multi-process": "^1.3.1",
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-replace": "^1.0.0",
|
||||
"gulp-sourcemaps": "^2.6.0",
|
||||
"gulp-stylefmt": "^1.1.0",
|
||||
"gulp-stylelint": "^7.0.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-uglify-es": "^1.0.1",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^5.0.0",
|
||||
"gulp-watch": "^5.0.1",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"http-server": "^0.12.1",
|
||||
"image-size": "^0.6.2",
|
||||
|
@ -289,36 +286,38 @@
|
|||
"mocha-jsdom": "^1.1.0",
|
||||
"mocha-sinon": "^2.0.0",
|
||||
"nock": "^9.0.14",
|
||||
"node-sass": "^4.9.2",
|
||||
"nyc": "^14.1.1",
|
||||
"node-sass": "^4.12.0",
|
||||
"nyc": "^15.0.0",
|
||||
"path": "^0.12.7",
|
||||
"png-file-stream": "^1.1.0",
|
||||
"prepend-file": "^1.3.1",
|
||||
"prompt": "^1.0.0",
|
||||
"proxyquire": "2.0.1",
|
||||
"proxyquire": "^2.1.3",
|
||||
"qs": "^6.2.0",
|
||||
"qunitjs": "^2.4.1",
|
||||
"radgrad-jsdoc-template": "^1.1.3",
|
||||
"react-test-renderer": "^16.8.6",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-test-renderer": "^16.12.0",
|
||||
"react-testutils-additions": "^16.0.2",
|
||||
"redux-mock-store": "^1.5.3",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
"redux-test-utils": "^0.2.2",
|
||||
"resolve-url-loader": "^2.3.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"sass-loader": "^7.0.1",
|
||||
"selenium-webdriver": "^4.0.0-alpha.4",
|
||||
"selenium-webdriver": "^4.0.0-alpha.5",
|
||||
"shell-parallel": "^1.0.3",
|
||||
"sinon": "^5.0.0",
|
||||
"source-map": "^0.7.2",
|
||||
"static-server": "^2.2.1",
|
||||
"style-loader": "^0.21.0",
|
||||
"stylelint": "^9.10.1",
|
||||
"stylelint-config-standard": "^18.2.0",
|
||||
"tape": "^4.5.1",
|
||||
"testem": "^2.8.0",
|
||||
"testem": "^2.16.0",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"watchify": "^3.11.0"
|
||||
"watchify": "^3.11.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "12.15.0",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,14 +35,14 @@ module.exports = {
|
|||
account2: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span'),
|
||||
account3: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(4) > span'),
|
||||
account4: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(5) > span'),
|
||||
menu: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div'),
|
||||
menu: By.css('#app-content > div > div.full-width > div.full-width > div > div.app-bar-right-menus-section > span > div'),
|
||||
delete1: By.xpath('//*[@id="app-content"]/div/div[1]/div[1]/div/div[2]/span/div/div/span/div/li[5]/div[3]'),
|
||||
delete: By.className('remove'),
|
||||
createAccount: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span'),
|
||||
// import: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(5) > span'),
|
||||
import: By.css('li.dropdown-menu-item:nth-child(4) > span:nth-child(1)'),
|
||||
// import22: By.css('#app-content > div > div.full-width > div.full-width > div > div.app-bar-right-menus-section > span > div > div > span > div > li:nth-child(4) > span'),
|
||||
import2: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(5)'),
|
||||
// import3: By.css('#app-content > div > div.full-width > div.full-width > div > div.app-bar-right-menus-section > span > div > div > span > div > li:nth-child(5) > span'),
|
||||
label: By.className('keyring-label'),
|
||||
},
|
||||
dot: {
|
||||
|
@ -240,11 +240,11 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
deleteImportedAccount: {
|
||||
title: By.css('#app-content > div > div.app-primary.from-left > div > div.section-title.flex-row.flex-center > h2'),
|
||||
title: By.className('page-subtitle'),
|
||||
titleText: 'Delete Imported Account',
|
||||
buttons: {
|
||||
no: By.css('#app-content > div > div.app-primary.from-left > div > div.flex-row.flex-right > button.btn-violet'),
|
||||
yes: By.css('div.flex-row:nth-child(4) > button:nth-child(2)'),
|
||||
yes: By.css('#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-right > button:nth-child(2)'),
|
||||
arrow: By.className('fa fa-arrow-left fa-lg cursor-pointer'),
|
||||
},
|
||||
},
|
||||
|
@ -289,6 +289,7 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
main: {
|
||||
container: By.css('#app-content > div > div.app-primary.from-right > div'),
|
||||
accountAddress: By.xpath('//*[@id="app-content"]/div/div[2]/div/div/div[1]/flex-column/div[2]/div/span'),
|
||||
identicon: By.className('identicon-wrapper select-none'),
|
||||
fieldAccountName: By.className('sizing-input'),
|
||||
|
|
|
@ -6,10 +6,10 @@ 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 { accountCreation } = 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 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`)
|
||||
|
@ -20,6 +20,7 @@ const checkEmittedEvents = require(`${testsFolder}/check-emitted-events.spec`)
|
|||
const changePassword = require(`${testsFolder}/change-password.spec`)
|
||||
// const addTokenFromSearch = require(`${testsFolder}/add-token-search.spec`)
|
||||
const customRPC = require(`${testsFolder}/custom-rpc.spec`)
|
||||
const { buildWebDriver } = require(`./webdriver`)
|
||||
|
||||
describe('Metamask popup page', async function () {
|
||||
|
||||
|
@ -35,13 +36,13 @@ describe('Metamask popup page', async function () {
|
|||
|
||||
before(async function () {
|
||||
if (process.env.SELENIUM_BROWSER === 'chrome') {
|
||||
const extPath = path.resolve('dist/chrome')
|
||||
driver = await Func.buildChromeWebDriver(extPath)
|
||||
const { driver: chromeDriver, extensionId: _extensionId } = await buildWebDriver({responsive: false})
|
||||
const extensionUrl = chromeDriver.extensionUrl
|
||||
driver = chromeDriver.driver
|
||||
extensionId = _extensionId
|
||||
f.driver = driver
|
||||
extensionId = await f.getExtensionIdChrome()
|
||||
f.extensionId = extensionId
|
||||
await driver.get(`chrome-extension://${extensionId}/popup.html`)
|
||||
|
||||
await f.driver.get(extensionUrl)
|
||||
} else if (process.env.SELENIUM_BROWSER === 'firefox') {
|
||||
const extPath = path.resolve('dist/firefox')
|
||||
driver = await Func.buildFirefoxWebdriver()
|
||||
|
@ -74,7 +75,7 @@ describe('Metamask popup page', async function () {
|
|||
})
|
||||
|
||||
after(async function () {
|
||||
await driver.quit()
|
||||
await f.driver.quit()
|
||||
})
|
||||
|
||||
describe('Setup', async () => {
|
||||
|
@ -97,9 +98,9 @@ describe('Metamask popup page', async function () {
|
|||
await importAccount(f)
|
||||
})
|
||||
|
||||
describe('Import Contract account', async () => {
|
||||
await importContractAccount(f, account1, getCreatedAccounts)
|
||||
})
|
||||
// describe('Import Contract account', async () => {
|
||||
// await importContractAccount(f, account1, getCreatedAccounts)
|
||||
// })
|
||||
|
||||
describe('Delete Imported Account', async () => {
|
||||
await deleteImportedAccount(f)
|
||||
|
|
|
@ -15,9 +15,10 @@ const checkEmittedEvents = async (f, account1, account2) => {
|
|||
const item = await f.waitUntilShowUp(menus.account.account2)
|
||||
await item.click()
|
||||
}
|
||||
await f.driver.navigate().refresh()
|
||||
|
||||
const balanceField = await f.waitUntilShowUp(screens.main.balance)
|
||||
await f.delay(2000)
|
||||
await f.delay(5000)
|
||||
const balance = await balanceField.getText()
|
||||
console.log('Account = ' + account)
|
||||
console.log('Balance = ' + balance)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const assert = require('assert')
|
||||
const { menus, screens } = require('../elements')
|
||||
const { account } = menus
|
||||
const { deleteImportedAccount: deleteImportedAccountScr, settings } = screens
|
||||
const { deleteImportedAccount: deleteImportedAccountScr, main } = screens
|
||||
|
||||
const deleteImportedAccount = async (f) => {
|
||||
it('Open delete imported account screen', async function () {
|
||||
|
@ -9,6 +9,7 @@ const deleteImportedAccount = async (f) => {
|
|||
await menu.click()
|
||||
const item = await f.waitUntilShowUp(account.delete)
|
||||
await item.click()
|
||||
await f.delay(2000)
|
||||
const deleteImportedAccountTitle = await f.waitUntilShowUp(deleteImportedAccountScr.title)
|
||||
assert.equal(await deleteImportedAccountTitle.getText(), deleteImportedAccountScr.titleText)
|
||||
})
|
||||
|
@ -17,8 +18,7 @@ const deleteImportedAccount = async (f) => {
|
|||
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')
|
||||
await f.driver.findElements(main.container)
|
||||
// check, that imported account still exists
|
||||
const menu = await f.waitUntilShowUp(account.menu)
|
||||
await menu.click()
|
||||
|
@ -43,8 +43,7 @@ const deleteImportedAccount = async (f) => {
|
|||
assert.equal(await button.getText(), 'Yes', 'button has incorrect name')
|
||||
await f.click(button)
|
||||
await f.delay(2000)
|
||||
const settingsTitle = await f.waitUntilShowUp(settings.title)
|
||||
assert.equal(await settingsTitle.getText(), 'Settings', "screen 'Settings' has incorrect title")
|
||||
await f.driver.findElements(main.container)
|
||||
// check, that imported account is removed
|
||||
const menu = await f.waitUntilShowUp(account.menu)
|
||||
await menu.click()
|
||||
|
|
|
@ -13,7 +13,7 @@ const addrPrivKey = '76bd0ced0a47055bb5d060e1ae4a8cb3ece658d668823e250dae6e79d3a
|
|||
const importAccount = async (f) => {
|
||||
it('Open import account menu', async () => {
|
||||
await f.setProvider(NETWORKS.POA)
|
||||
await f.delay(2000)
|
||||
await f.delay(5000)
|
||||
const menu = await f.waitUntilShowUp(account.menu)
|
||||
await menu.click()
|
||||
await f.delay(2000)
|
||||
|
@ -39,9 +39,9 @@ const importAccount = async (f) => {
|
|||
})
|
||||
|
||||
it('Auto-detect tokens for POA ', async () => {
|
||||
// await setProvider(NETWORKS.POA)
|
||||
const tab = await f.waitUntilShowUp(tokensEl.menu)
|
||||
await tab.click()
|
||||
await f.delay(15000)
|
||||
const balance = await f.waitUntilShowUp(tokensEl.balance)
|
||||
console.log(await balance.getText())
|
||||
assert.equal(await balance.getText(), '1 DOPR', 'token isnt\' auto-detected')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const assert = require('assert')
|
||||
const clipboardy = require('clipboardy')
|
||||
const { menus, screens, elements, NETWORKS } = require('../elements')
|
||||
const { main } = screens
|
||||
let abiClipboard
|
||||
|
||||
const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
||||
|
@ -36,16 +37,16 @@ const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
|||
})
|
||||
|
||||
it('ABI of Proxy + Implementation is fetched and matches the pattern', async () => {
|
||||
await f.delay(5000)
|
||||
await f.delay(10000)
|
||||
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)
|
||||
await f.delay(7000)
|
||||
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
|
||||
assert.notEqual(ident, false, "main screen isn't opened")
|
||||
})
|
||||
|
@ -93,13 +94,11 @@ const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
|||
assert.equal(text1, '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[accountPosition].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 deleteButton = await f.waitUntilShowUp(menus.account.delete)
|
||||
await deleteButton.click()
|
||||
const yesButton = await f.waitUntilShowUp(screens.deleteImportedAccount.buttons.yes)
|
||||
await yesButton.click()
|
||||
await f.driver.findElements(main.container)
|
||||
const identicon = await f.waitUntilShowUp(screens.main.identicon)
|
||||
assert.notEqual(identicon, false, 'main screen didn\'t opened')
|
||||
})
|
||||
|
@ -117,17 +116,12 @@ const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
|||
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 importItem = await f.waitUntilShowUp(menus.account.import2)
|
||||
await importItem.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)
|
||||
|
@ -208,6 +202,7 @@ const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
|||
it("Click button 'Import', main screen opens", async () => {
|
||||
const button = await f.waitUntilShowUp(screens.importAccounts.buttonImport)
|
||||
await f.click(button)
|
||||
await f.delay(5000)
|
||||
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
|
||||
assert.notEqual(ident, false, "main screen isn't opened")
|
||||
})
|
||||
|
@ -865,8 +860,7 @@ const importContractAccount = async (f, account1, getCreatedAccounts) => {
|
|||
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()
|
||||
await f.driver.findElements(main.container)
|
||||
const identicon = await f.waitUntilShowUp(screens.main.identicon)
|
||||
assert.notEqual(identicon, false, 'main screen didn\'t opened')
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ const login = async (f, password) => {
|
|||
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')
|
||||
assert.equal(text.length > 100, true, 'agreement is too short')
|
||||
})
|
||||
|
||||
it('screen \'Terms of Use\' has correct title', async () => {
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
const { Builder } = require('selenium-webdriver')
|
||||
const chrome = require('selenium-webdriver/chrome')
|
||||
|
||||
/**
|
||||
* A wrapper around a {@code WebDriver} instance exposing Chrome-specific functionality
|
||||
*/
|
||||
class ChromeDriver {
|
||||
static async build ({ extensionPath, responsive, port }) {
|
||||
const args = [
|
||||
`load-extension=${extensionPath}`,
|
||||
]
|
||||
if (responsive) {
|
||||
args.push('--auto-open-devtools-for-tabs')
|
||||
}
|
||||
const options = new chrome.Options()
|
||||
.addArguments(args)
|
||||
const builder = new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(options)
|
||||
if (port) {
|
||||
const service = new chrome.ServiceBuilder()
|
||||
.setPort(port)
|
||||
builder.setChromeService(service)
|
||||
}
|
||||
const driver = builder.build()
|
||||
const chromeDriver = new ChromeDriver(driver)
|
||||
const extensionId = await chromeDriver.getExtensionIdByName('Nifty Wallet')
|
||||
|
||||
return {
|
||||
driver,
|
||||
extensionUrl: `chrome-extension://${extensionId}/popup.html`,
|
||||
extensionId: extensionId,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {!ThenableWebDriver} driver - a {@code WebDriver} instance
|
||||
*/
|
||||
constructor (driver) {
|
||||
this._driver = driver
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension ID for the given extension name
|
||||
* @param {string} extensionName - the extension name
|
||||
* @returns {Promise<string|undefined>} - the extension ID
|
||||
*/
|
||||
async getExtensionIdByName (extensionName) {
|
||||
await this._driver.get('chrome://extensions')
|
||||
return await this._driver.executeScript(`
|
||||
const extensions = document.querySelector("extensions-manager").shadowRoot
|
||||
.querySelector("extensions-item-list").shadowRoot
|
||||
.querySelectorAll("extensions-item")
|
||||
|
||||
for (let i = 0; i < extensions.length; i++) {
|
||||
const extension = extensions[i].shadowRoot
|
||||
const name = extension.querySelector('#name').textContent
|
||||
if (name === "${extensionName}") {
|
||||
return extensions[i].getAttribute("id")
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ChromeDriver
|
|
@ -0,0 +1,189 @@
|
|||
const { promises: fs } = require('fs')
|
||||
const { until, error: webdriverError } = require('selenium-webdriver')
|
||||
const { strict: assert } = require('assert')
|
||||
|
||||
class Driver {
|
||||
/**
|
||||
* @param {!ThenableWebDriver} driver - A {@code WebDriver} instance
|
||||
* @param {string} browser - The type of browser this driver is controlling
|
||||
* @param {number} timeout
|
||||
*/
|
||||
constructor (driver, browser, extensionUrl, timeout = 10000) {
|
||||
this.driver = driver
|
||||
this.browser = browser
|
||||
this.extensionUrl = extensionUrl
|
||||
this.timeout = timeout
|
||||
}
|
||||
|
||||
async delay (time) {
|
||||
await new Promise(resolve => setTimeout(resolve, time))
|
||||
}
|
||||
|
||||
async wait (condition, timeout = this.timeout) {
|
||||
await this.driver.wait(condition, timeout)
|
||||
}
|
||||
|
||||
async quit () {
|
||||
await this.driver.quit()
|
||||
}
|
||||
|
||||
// Element interactions
|
||||
|
||||
async findElement (locator) {
|
||||
return await this.driver.wait(until.elementLocated(locator), this.timeout)
|
||||
}
|
||||
|
||||
async findVisibleElement (locator) {
|
||||
const element = await this.findElement(locator)
|
||||
await this.driver.wait(until.elementIsVisible(element), this.timeout)
|
||||
return element
|
||||
}
|
||||
|
||||
async findClickableElement (locator) {
|
||||
const element = await this.findElement(locator)
|
||||
await Promise.all([
|
||||
this.driver.wait(until.elementIsVisible(element), this.timeout),
|
||||
this.driver.wait(until.elementIsEnabled(element), this.timeout),
|
||||
])
|
||||
return element
|
||||
}
|
||||
|
||||
async findElements (locator) {
|
||||
return await this.driver.wait(until.elementsLocated(locator), this.timeout)
|
||||
}
|
||||
|
||||
async findClickableElements (locator) {
|
||||
const elements = await this.findElements(locator)
|
||||
await Promise.all(elements
|
||||
.reduce((acc, element) => {
|
||||
acc.push(
|
||||
this.driver.wait(until.elementIsVisible(element), this.timeout),
|
||||
this.driver.wait(until.elementIsEnabled(element), this.timeout),
|
||||
)
|
||||
return acc
|
||||
}, [])
|
||||
)
|
||||
return elements
|
||||
}
|
||||
|
||||
async clickElement (locator) {
|
||||
const element = await this.findClickableElement(locator)
|
||||
await element.click()
|
||||
}
|
||||
|
||||
async scrollToElement (element) {
|
||||
await this.driver.executeScript('arguments[0].scrollIntoView(true)', element)
|
||||
}
|
||||
|
||||
async assertElementNotPresent (locator) {
|
||||
let dataTab
|
||||
try {
|
||||
dataTab = await this.findElement(locator)
|
||||
} catch (err) {
|
||||
assert(err instanceof webdriverError.NoSuchElementError || err instanceof webdriverError.TimeoutError)
|
||||
}
|
||||
assert.ok(!dataTab, 'Found element that should not be present')
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
async navigate (page = Driver.PAGES.HOME) {
|
||||
return await this.driver.get(`${this.extensionUrl}/${page}.html`)
|
||||
}
|
||||
|
||||
// Window management
|
||||
|
||||
async openNewPage (url) {
|
||||
const newHandle = await this.driver.switchTo().newWindow()
|
||||
await this.driver.get(url)
|
||||
return newHandle
|
||||
}
|
||||
|
||||
async switchToWindow (handle) {
|
||||
await this.driver.switchTo().window(handle)
|
||||
}
|
||||
|
||||
async getAllWindowHandles () {
|
||||
return await this.driver.getAllWindowHandles()
|
||||
}
|
||||
|
||||
async waitUntilXWindowHandles (x, delayStep = 1000, timeout = 5000) {
|
||||
let timeElapsed = 0
|
||||
while (timeElapsed <= timeout) {
|
||||
const windowHandles = await this.driver.getAllWindowHandles()
|
||||
if (windowHandles.length === x) {
|
||||
return
|
||||
}
|
||||
await this.delay(delayStep)
|
||||
timeElapsed += delayStep
|
||||
}
|
||||
throw new Error('waitUntilXWindowHandles timed out polling window handles')
|
||||
}
|
||||
|
||||
async switchToWindowWithTitle (title, windowHandles) {
|
||||
if (!windowHandles) {
|
||||
windowHandles = await this.driver.getAllWindowHandles()
|
||||
}
|
||||
|
||||
for (const handle of windowHandles) {
|
||||
await this.driver.switchTo().window(handle)
|
||||
const handleTitle = await this.driver.getTitle()
|
||||
if (handleTitle === title) {
|
||||
return handle
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No window with title: ' + title)
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all windows except those in the given list of exceptions
|
||||
* @param {Array<string>} exceptions - The list of window handle exceptions
|
||||
* @param {Array} [windowHandles] - The full list of window handles
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async closeAllWindowHandlesExcept (exceptions, windowHandles) {
|
||||
windowHandles = windowHandles || await this.driver.getAllWindowHandles()
|
||||
|
||||
for (const handle of windowHandles) {
|
||||
if (!exceptions.includes(handle)) {
|
||||
await this.driver.switchTo().window(handle)
|
||||
await this.delay(1000)
|
||||
await this.driver.close()
|
||||
await this.delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error handling
|
||||
|
||||
async verboseReportOnFailure (test) {
|
||||
const artifactDir = `./test-artifacts/${this.browser}/${test.title}`
|
||||
const filepathBase = `${artifactDir}/test-failure`
|
||||
await fs.mkdir(artifactDir, { recursive: true })
|
||||
const screenshot = await this.driver.takeScreenshot()
|
||||
await fs.writeFile(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
|
||||
const htmlSource = await this.driver.getPageSource()
|
||||
await fs.writeFile(`${filepathBase}-dom.html`, htmlSource)
|
||||
}
|
||||
|
||||
async checkBrowserForConsoleErrors () {
|
||||
const ignoredLogTypes = ['WARNING']
|
||||
const ignoredErrorMessages = [
|
||||
// Third-party Favicon 404s show up as errors
|
||||
'favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)',
|
||||
]
|
||||
const browserLogs = await this.driver.manage().logs().get('browser')
|
||||
const errorEntries = browserLogs.filter(entry => !ignoredLogTypes.includes(entry.level.toString()))
|
||||
const errorObjects = errorEntries.map(entry => entry.toJSON())
|
||||
return errorObjects.filter(entry => !ignoredErrorMessages.some(message => entry.message.includes(message)))
|
||||
}
|
||||
}
|
||||
|
||||
Driver.PAGES = {
|
||||
HOME: 'home',
|
||||
NOTIFICATION: 'notification',
|
||||
POPUP: 'popup',
|
||||
}
|
||||
|
||||
module.exports = Driver
|
|
@ -0,0 +1,103 @@
|
|||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const { Builder, By, until } = require('selenium-webdriver')
|
||||
const firefox = require('selenium-webdriver/firefox')
|
||||
const { Command } = require('selenium-webdriver/lib/command')
|
||||
|
||||
/**
|
||||
* The prefix for temporary Firefox profiles. All Firefox profiles used for e2e tests
|
||||
* will be created as random directories inside this.
|
||||
* @type {string}
|
||||
*/
|
||||
const TEMP_PROFILE_PATH_PREFIX = path.join(os.tmpdir(), 'MetaMask-Fx-Profile')
|
||||
|
||||
const GeckoDriverCommand = {
|
||||
INSTALL_ADDON: 'install addon',
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around a {@code WebDriver} instance exposing Firefox-specific functionality
|
||||
*/
|
||||
class FirefoxDriver {
|
||||
/**
|
||||
* Builds a {@link FirefoxDriver} instance
|
||||
* @param {{extensionPath: string}} options - the options for the build
|
||||
* @returns {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>}
|
||||
*/
|
||||
static async build ({ extensionPath, responsive, port }) {
|
||||
const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX)
|
||||
const options = new firefox.Options()
|
||||
.setProfile(templateProfile)
|
||||
const builder = new Builder()
|
||||
.forBrowser('firefox')
|
||||
.setFirefoxOptions(options)
|
||||
if (port) {
|
||||
const service = new firefox.ServiceBuilder()
|
||||
.setPort(port)
|
||||
builder.setFirefoxService(service)
|
||||
}
|
||||
const driver = builder.build()
|
||||
const fxDriver = new FirefoxDriver(driver)
|
||||
|
||||
await fxDriver.init()
|
||||
|
||||
const extensionId = await fxDriver.installExtension(extensionPath)
|
||||
const internalExtensionId = await fxDriver.getInternalId()
|
||||
|
||||
if (responsive) {
|
||||
await driver.manage().window().setRect({ width: 320, height: 600 })
|
||||
}
|
||||
|
||||
return {
|
||||
driver,
|
||||
extensionId,
|
||||
extensionUrl: `moz-extension://${internalExtensionId}`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {!ThenableWebDriver} driver - a {@code WebDriver} instance
|
||||
*/
|
||||
constructor (driver) {
|
||||
this._driver = driver
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the driver
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async init () {
|
||||
await this._driver.getExecutor()
|
||||
.defineCommand(
|
||||
GeckoDriverCommand.INSTALL_ADDON,
|
||||
'POST',
|
||||
'/session/:sessionId/moz/addon/install',
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the extension at the given path
|
||||
* @param {string} addonPath - the path to the unpacked extension or XPI
|
||||
* @returns {Promise<string>} - the extension ID
|
||||
*/
|
||||
async installExtension (addonPath) {
|
||||
const cmd = new Command(GeckoDriverCommand.INSTALL_ADDON)
|
||||
.setParameter('path', path.resolve(addonPath))
|
||||
.setParameter('temporary', true)
|
||||
|
||||
return await this._driver.execute(cmd)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Internal UUID for the given extension
|
||||
* @returns {Promise<string>} - the Internal UUID for the given extension
|
||||
*/
|
||||
async getInternalId () {
|
||||
await this._driver.get('about:debugging#addons')
|
||||
return await this._driver.wait(until.elementLocated(By.xpath('//dl/div[contains(., \'Internal UUID\')]/dd')), 1000).getText()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FirefoxDriver
|
|
@ -0,0 +1,69 @@
|
|||
const { Browser } = require('selenium-webdriver')
|
||||
const Driver = require('./driver')
|
||||
const ChromeDriver = require('./chrome')
|
||||
const FirefoxDriver = require('./firefox')
|
||||
const fetchMockResponses = require('../../data/fetch-mocks.json')
|
||||
|
||||
async function buildWebDriver ({ responsive, port } = {}) {
|
||||
const browser = process.env.SELENIUM_BROWSER
|
||||
const extensionPath = `dist/${browser}`
|
||||
|
||||
const { driver: seleniumDriver, extensionId, extensionUrl } = await buildBrowserWebDriver(browser, { extensionPath, responsive, port })
|
||||
setupFetchMocking(seleniumDriver)
|
||||
const driver = new Driver(seleniumDriver, browser, extensionUrl)
|
||||
|
||||
return {
|
||||
driver,
|
||||
extensionId,
|
||||
}
|
||||
}
|
||||
|
||||
async function buildBrowserWebDriver (browser, webDriverOptions) {
|
||||
switch (browser) {
|
||||
case Browser.CHROME: {
|
||||
return await ChromeDriver.build(webDriverOptions)
|
||||
}
|
||||
case Browser.FIREFOX: {
|
||||
return await FirefoxDriver.build(webDriverOptions)
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unrecognized browser: ${browser}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupFetchMocking (driver) {
|
||||
// define fetchMocking script, to be evaluated in the browser
|
||||
function fetchMocking (fetchMockResponses) {
|
||||
window.origFetch = window.fetch.bind(window)
|
||||
window.fetch = async (...args) => {
|
||||
const url = args[0]
|
||||
if (url === 'https://ethgasstation.info/json/ethgasAPI.json') {
|
||||
return { json: async () => clone(fetchMockResponses.ethGasBasic) }
|
||||
} else if (url === 'https://ethgasstation.info/json/predictTable.json') {
|
||||
return { json: async () => clone(fetchMockResponses.ethGasPredictTable) }
|
||||
} else if (url.match(/chromeextensionmm/)) {
|
||||
return { json: async () => clone(fetchMockResponses.metametrics) }
|
||||
}
|
||||
return window.origFetch(...args)
|
||||
}
|
||||
if (window.chrome && window.chrome.webRequest) {
|
||||
window.chrome.webRequest.onBeforeRequest.addListener(cancelInfuraRequest, { urls: ['https://*.infura.io/*'] }, ['blocking'])
|
||||
}
|
||||
function cancelInfuraRequest (requestDetails) {
|
||||
console.log(`fetchMocking - Canceling request: "${requestDetails.url}"`)
|
||||
return { cancel: true }
|
||||
}
|
||||
function clone (obj) {
|
||||
return JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
}
|
||||
// fetchMockResponses are parsed last minute to ensure that objects are uniquely instantiated
|
||||
const fetchMockResponsesJson = JSON.stringify(fetchMockResponses)
|
||||
// eval the fetchMocking script in the browser
|
||||
await driver.executeScript(`(${fetchMocking})(${fetchMockResponsesJson})`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildWebDriver,
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
require('babel-register')({
|
||||
ignore: name => name.includes('node_modules') && !name.includes('obs-store'),
|
||||
require('@babel/register')({
|
||||
ignore: [(name) => name.includes('node_modules') && !name.includes('obs-store')],
|
||||
})
|
||||
|
||||
require('./helper')
|
||||
|
|
|
@ -830,6 +830,7 @@ function reduceApp (state, action) {
|
|||
context: appState.currentView.context,
|
||||
},
|
||||
identity: action.identity,
|
||||
keyring: action.keyring,
|
||||
})
|
||||
|
||||
case actions.CONFIRM_CHANGE_PASSWORD:
|
||||
|
|
Loading…
Reference in New Issue