Merge pull request #332 from poanetwork/vb-fix-remove-acc

Return to main screen after removal of imported account
This commit is contained in:
Victor Baranov 2020-03-21 10:13:59 +03:00 committed by GitHub
commit fd1e6c9c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 15701 additions and 11951 deletions

View File

@ -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"]
}

View File

@ -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:

View File

@ -27,8 +27,8 @@
},
"plugins": [
"babel",
"mocha",
"chai",
"react",
"json"
],

View File

@ -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

View File

@ -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`.

View File

@ -110,7 +110,7 @@ class ExtensionPlatform {
})
}
_subscribeToNotificationClicked = () => {
_subscribeToNotificationClicked () {
if (extension.notifications.onClicked.hasListener(this._viewOnExplorer)) {
extension.notifications.onClicked.removeListener(this._viewOnExplorer)
}

25
babel.config.js Normal file
View File

@ -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',
],
}
}

View File

@ -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',
},
})
```

View File

@ -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 = {

View File

@ -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)**

View File

@ -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))

View File

@ -39,6 +39,7 @@ export default class AddTokenScreen extends Component {
address: PropTypes.string,
dispatch: PropTypes.func,
network: PropTypes.string,
addToken: PropTypes.func,
}
constructor (props) {

View File

@ -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 () {

View File

@ -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%' }}>

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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 {

View File

@ -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',
},
}),
]),

View File

@ -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: {

View File

@ -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

View File

@ -38,6 +38,7 @@ class ConfigScreen extends Component {
static propTypes = {
dispatch: PropTypes.func,
metamask: PropTypes.object,
warning: PropTypes.string,
}
render () {

View File

@ -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%',

20874
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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",

5922
test/data/fetch-mocks.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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'),

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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')

View File

@ -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')
})

View File

@ -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 () => {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
}

View File

@ -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')

View File

@ -830,6 +830,7 @@ function reduceApp (state, action) {
context: appState.currentView.context,
},
identity: action.identity,
keyring: action.keyring,
})
case actions.CONFIRM_CHANGE_PASSWORD: