From 4963ed65c0ee6f827fa6302079d4d8e0a4fdb0aa Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 5 Apr 2019 00:46:25 -0230 Subject: Track seed phrase validation errors with MetaMetrics --- .../import-with-seed-phrase.component.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js index 433dad6e2..96ff11eaf 100644 --- a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js +++ b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js @@ -36,6 +36,20 @@ export default class ImportWithSeedPhrase extends PureComponent { .join(' ') } + componentWillMount () { + window.onbeforeunload = () => this.context.metricsEvent({ + eventOpts: { + category: 'Onboarding', + action: 'Import Seed Phrase', + name: 'Close window on import screen', + }, + customVariables: { + errorLabel: 'Seed Phrase Error', + errorMessage: this.state.seedPhraseError, + }, + }) + } + handleSeedPhraseChange (seedPhrase) { let seedPhraseError = '' @@ -172,6 +186,10 @@ export default class ImportWithSeedPhrase extends PureComponent { action: 'Import Seed Phrase', name: 'Go Back from Onboarding Import', }, + customVariables: { + errorLabel: 'Seed Phrase Error', + errorMessage: seedPhraseError, + }, }) this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE) }} -- cgit From 5454266d7c5f49c3cce59a673674371bb5ccfb7f Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 5 Apr 2019 01:32:47 -0230 Subject: Metrics tracking gas changed - slow, average, fast, custom - on edit screen.- --- .../components/app/send/send-footer/send-footer.component.js | 5 +++++ .../components/app/send/send-footer/send-footer.container.js | 12 ++++++++++++ .../app/send/send-footer/tests/send-footer-container.test.js | 5 +++++ ui/app/helpers/utils/metametrics.util.js | 2 ++ 4 files changed, 24 insertions(+) diff --git a/ui/app/components/app/send/send-footer/send-footer.component.js b/ui/app/components/app/send/send-footer/send-footer.component.js index cc891a9b3..7d894391f 100644 --- a/ui/app/components/app/send/send-footer/send-footer.component.js +++ b/ui/app/components/app/send/send-footer/send-footer.component.js @@ -27,6 +27,7 @@ export default class SendFooter extends Component { unapprovedTxs: PropTypes.object, update: PropTypes.func, sendErrors: PropTypes.object, + gasChangedLabel: PropTypes.string, } static contextTypes = { @@ -57,6 +58,7 @@ export default class SendFooter extends Component { update, toAccounts, history, + gasChangedLabel, } = this.props const { metricsEvent } = this.context @@ -91,6 +93,9 @@ export default class SendFooter extends Component { action: 'Edit Screen', name: 'Complete', }, + customVariables: { + gasChanged: gasChangedLabel, + }, }) history.push(CONFIRM_TRANSACTION_ROUTE) }) diff --git a/ui/app/components/app/send/send-footer/send-footer.container.js b/ui/app/components/app/send/send-footer/send-footer.container.js index ea3fd7ee4..4757f6bec 100644 --- a/ui/app/components/app/send/send-footer/send-footer.container.js +++ b/ui/app/components/app/send/send-footer/send-footer.container.js @@ -31,10 +31,21 @@ import { constructTxParams, constructUpdatedTx, } from './send-footer.utils' +import { + getRenderableEstimateDataForSmallButtonsFromGWEI, + getDefaultActiveButtonIndex, +} from '../../../../selectors/custom-gas' export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) function mapStateToProps (state) { + const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state) + const gasPrice = getGasPrice(state) + const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, gasPrice) + const gasChangedLabel = activeButtonIndex >= 0 + ? gasButtonInfo[activeButtonIndex].labelKey + : 'custom' + return { amount: getSendAmount(state), data: getSendHexData(state), @@ -50,6 +61,7 @@ function mapStateToProps (state) { tokenBalance: getTokenBalance(state), unapprovedTxs: getUnapprovedTxs(state), sendErrors: getSendErrors(state), + gasChangedLabel, } } diff --git a/ui/app/components/app/send/send-footer/tests/send-footer-container.test.js b/ui/app/components/app/send/send-footer/tests/send-footer-container.test.js index 878b0aa19..64c6451f2 100644 --- a/ui/app/components/app/send/send-footer/tests/send-footer-container.test.js +++ b/ui/app/components/app/send/send-footer/tests/send-footer-container.test.js @@ -46,6 +46,10 @@ proxyquire('../send-footer.container.js', { }, './send-footer.selectors': { isSendFormInError: (s) => `mockInError:${s}` }, './send-footer.utils': utilsStubs, + '../../../../selectors/custom-gas': { + getRenderableEstimateDataForSmallButtonsFromGWEI: (s) => ([{ labelKey: `mockLabel:${s}` }]), + getDefaultActiveButtonIndex: () => 0, + }, }) describe('send-footer container', () => { @@ -68,6 +72,7 @@ describe('send-footer container', () => { tokenBalance: 'mockTokenBalance:mockState', unapprovedTxs: 'mockUnapprovedTxs:mockState', sendErrors: 'mockSendErrors:mockState', + gasChangedLabel: 'mockLabel:mockState', }) }) diff --git a/ui/app/helpers/utils/metametrics.util.js b/ui/app/helpers/utils/metametrics.util.js index 01984bd5e..5ae3e8937 100644 --- a/ui/app/helpers/utils/metametrics.util.js +++ b/ui/app/helpers/utils/metametrics.util.js @@ -23,6 +23,7 @@ const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField' const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage' const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId' const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId' +const METAMETRICS_CUSTOM_GAS_CHANGED = 'gasChanged' const METAMETRICS_CUSTOM_NETWORK = 'network' const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType' @@ -43,6 +44,7 @@ const customVariableNameIdMap = { [METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 2, [METAMETRICS_CUSTOM_ERROR_FIELD]: 1, [METAMETRICS_CUSTOM_ERROR_MESSAGE]: 2, + [METAMETRICS_CUSTOM_GAS_CHANGED]: 1, } const customDimensionsNameIdMap = { -- cgit From 5d948360c06c250dcf7261c8624f792a8c21fbae Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 8 Apr 2019 10:52:26 -0230 Subject: Distinguish between token and eth selected in home screen send button metrics event. --- .../app/transaction-view-balance/transaction-view-balance.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/app/transaction-view-balance/transaction-view-balance.component.js b/ui/app/components/app/transaction-view-balance/transaction-view-balance.component.js index 8559e2233..fa63b6fd3 100644 --- a/ui/app/components/app/transaction-view-balance/transaction-view-balance.component.js +++ b/ui/app/components/app/transaction-view-balance/transaction-view-balance.component.js @@ -112,7 +112,7 @@ export default class TransactionViewBalance extends PureComponent { eventOpts: { category: 'Navigation', action: 'Home', - name: 'Clicked Send', + name: selectedToken ? 'Clicked Send: Token' : 'Clicked Send: Eth', }, }) history.push(SEND_ROUTE) -- cgit From c80b295ccccc5d57a5dfe0ca8cc2d663609b7a26 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 8 Apr 2019 11:32:51 -0230 Subject: Only pass english function names to functionType metric --- .../confirm-deploy-contract.component.js | 2 +- .../confirm-send-ether.component.js | 2 +- .../confirm-transaction-base.component.js | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/app/pages/confirm-deploy-contract/confirm-deploy-contract.component.js b/ui/app/pages/confirm-deploy-contract/confirm-deploy-contract.component.js index 9bc0daab9..c90ccc917 100644 --- a/ui/app/pages/confirm-deploy-contract/confirm-deploy-contract.component.js +++ b/ui/app/pages/confirm-deploy-contract/confirm-deploy-contract.component.js @@ -56,7 +56,7 @@ export default class ConfirmDeployContract extends Component { render () { return ( ) diff --git a/ui/app/pages/confirm-send-ether/confirm-send-ether.component.js b/ui/app/pages/confirm-send-ether/confirm-send-ether.component.js index 8daad675e..68280f624 100644 --- a/ui/app/pages/confirm-send-ether/confirm-send-ether.component.js +++ b/ui/app/pages/confirm-send-ether/confirm-send-ether.component.js @@ -30,7 +30,7 @@ export default class ConfirmSendEther extends Component { return ( this.handleEdit(confirmTransactionData)} /> diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js index 9e749322f..5cafe91c9 100644 --- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -64,7 +64,7 @@ export default class ConfirmTransactionBase extends Component { updateGasAndCalculate: PropTypes.func, customGas: PropTypes.object, // Component props - action: PropTypes.string, + actionKey: PropTypes.string, contentComponent: PropTypes.node, dataComponent: PropTypes.node, detailsComponent: PropTypes.node, @@ -159,7 +159,7 @@ export default class ConfirmTransactionBase extends Component { } handleEditGas () { - const { onEditGas, showCustomizeGasModal, action, txData: { origin }, methodData = {} } = this.props + const { onEditGas, showCustomizeGasModal, actionKey, txData: { origin }, methodData = {} } = this.props this.context.metricsEvent({ eventOpts: { @@ -169,7 +169,7 @@ export default class ConfirmTransactionBase extends Component { }, customVariables: { recipientKnown: null, - functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'), + functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction', origin, }, }) @@ -292,7 +292,7 @@ export default class ConfirmTransactionBase extends Component { } handleEdit () { - const { txData, tokenData, tokenProps, onEdit, action, txData: { origin }, methodData = {} } = this.props + const { txData, tokenData, tokenProps, onEdit, actionKey, txData: { origin }, methodData = {} } = this.props this.context.metricsEvent({ eventOpts: { @@ -302,7 +302,7 @@ export default class ConfirmTransactionBase extends Component { }, customVariables: { recipientKnown: null, - functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'), + functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction', origin, }, }) @@ -331,7 +331,7 @@ export default class ConfirmTransactionBase extends Component { handleCancel () { const { metricsEvent } = this.context - const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction, action, txData: { origin }, methodData = {} } = this.props + const { onCancel, txData, cancelTransaction, history, clearConfirmTransaction, actionKey, txData: { origin }, methodData = {} } = this.props if (onCancel) { metricsEvent({ @@ -342,7 +342,7 @@ export default class ConfirmTransactionBase extends Component { }, customVariables: { recipientKnown: null, - functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'), + functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction', origin, }, }) @@ -358,7 +358,7 @@ export default class ConfirmTransactionBase extends Component { handleSubmit () { const { metricsEvent } = this.context - const { txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, onSubmit, action, metaMetricsSendCount = 0, setMetaMetricsSendCount, methodData = {} } = this.props + const { txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, onSubmit, actionKey, metaMetricsSendCount = 0, setMetaMetricsSendCount, methodData = {} } = this.props const { submitting } = this.state if (submitting) { @@ -377,7 +377,7 @@ export default class ConfirmTransactionBase extends Component { }, customVariables: { recipientKnown: null, - functionType: action || getMethodName(methodData.name) || this.context.t('contractInteraction'), + functionType: actionKey || getMethodName(methodData.name) || 'contractInteraction', origin, }, }) @@ -517,7 +517,7 @@ export default class ConfirmTransactionBase extends Component { valid: propsValid = true, errorMessage, errorKey: propsErrorKey, - action, + actionKey, title, subtitle, hideSubtitle, @@ -543,7 +543,7 @@ export default class ConfirmTransactionBase extends Component { toName={toName} toAddress={toAddress} showEdit={onEdit && !isTxReprice} - action={action || getMethodName(name) || this.context.t('contractInteraction')} + action={this.context.t(actionKey) || getMethodName(name) || this.context.t('contractInteraction')} title={title} titleComponent={this.renderTitleComponent()} subtitle={subtitle} -- cgit From 7b13e9ae2e1756aca4c2a9ffb7d23026353c1d27 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Mon, 8 Apr 2019 15:43:34 -0400 Subject: Update porting_to_new_environment.md This MetamaskInpageProvider file was moved out into its own repo, this updates the link to point to that repo. --- docs/porting_to_new_environment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/porting_to_new_environment.md b/docs/porting_to_new_environment.md index d901f2b78..f7a2ac8b7 100644 --- a/docs/porting_to_new_environment.md +++ b/docs/porting_to_new_environment.md @@ -10,7 +10,7 @@ The `metamask-background` describes the file at `app/scripts/background.js`, whi When a new site is visited, the WebExtension creates a new `ContentScript` in that page's context, which can be seen at `app/scripts/contentscript.js`. This script represents a per-page setup process, which creates the per-page `web3` api, connects it to the background script via the Port API (wrapped in a [stream abstraction](https://github.com/substack/stream-handbook)), and injected into the DOM before anything loads. -The most confusing part about porting MetaMask to a new platform is the way we provide the Web3 API over a series of streams between contexts. Once you understand how we create the [InpageProvider](../app/scripts/lib/inpage-provider.js) in the [inpage.js script](../app/scripts/inpage.js), you will be able to understand how the [port-stream](../app/scripts/lib/port-stream.js) is just a thin wrapper around the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), and a similar stream API can be wrapped around any communication channel to communicate with the `MetaMaskController` via its `setupUntrustedCommunication(stream, domain)` method. +The most confusing part about porting MetaMask to a new platform is the way we provide the Web3 API over a series of streams between contexts. Once you understand how we create the [MetamaskInpageProvider](https://github.com/MetaMask/metamask-inpage-provider/blob/master/index.js) in the [inpage.js script](../app/scripts/inpage.js), you will be able to understand how the [port-stream](../app/scripts/lib/port-stream.js) is just a thin wrapper around the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), and a similar stream API can be wrapped around any communication channel to communicate with the `MetaMaskController` via its `setupUntrustedCommunication(stream, domain)` method. ### The MetaMask Controller @@ -89,7 +89,7 @@ MetaMask has two kinds of [duplex stream APIs](https://github.com/substack/strea If you are making a MetaMask-powered browser for a new platform, one of the trickiest tasks will be injecting the Web3 API into websites that are visited. On WebExtensions, we actually have to pipe data through a total of three JS contexts just to let sites talk to our background process (site -> contentscript -> background). -To see how we do that, you can refer to the [inpage script](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/inpage.js) that we inject into every website. There you can see it creates a multiplex stream to the background, and uses it to initialize what we call the [inpage-provider](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/lib/inpage-provider.js), which you can see stubs a few methods out, but mostly just passes calls to `sendAsync` through the stream it's passed! That's really all the magic that's needed to create a web3-like API in a remote context, once you have a stream to MetaMask available. +To see how we do that, you can refer to the [inpage script](https://github.com/MetaMask/metamask-extension/blob/master/app/scripts/inpage.js) that we inject into every website. There you can see it creates a multiplex stream to the background, and uses it to initialize what we call the [MetamaskInpageProvider](https://github.com/MetaMask/metamask-inpage-provider/blob/master/index.js), which you can see stubs a few methods out, but mostly just passes calls to `sendAsync` through the stream it's passed! That's really all the magic that's needed to create a web3-like API in a remote context, once you have a stream to MetaMask available. In `inpage.js` you can see we create a `PortStream`, that's just a class we use to wrap WebExtension ports as streams, so we can reuse our favorite stream abstraction over the more irregular API surface of the WebExtension. In a new platform, you will probably need to construct this stream differently. The key is that you need to construct a stream that talks from the site context to the background. Once you have that set up, it works like magic! -- cgit From 24761326de652e14533c8f51498a25e875ab429b Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 9 Apr 2019 14:14:04 -0230 Subject: Don't inject web3 on sharefile.com --- app/scripts/contentscript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 68b6117e5..2325cecdd 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -275,6 +275,7 @@ function blacklistedDomainCheck () { 'harbourair.com', 'ani.gamer.com.tw', 'blueskybooking.com', + 'sharefile.com', ] const currentUrl = window.location.href let currentRegex -- cgit From d7a2ea9a2b28678bf46dfb606187f4bbb7d22453 Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Wed, 10 Apr 2019 16:34:13 -0500 Subject: Add Localhost 8545 for network dropdown names --- app/_locales/en/messages.json | 3 +++ ui/app/components/app/dropdowns/network-dropdown.js | 2 ++ .../app/loading-network-screen/loading-network-screen.component.js | 2 ++ ui/app/components/app/network.js | 2 +- ui/app/pages/routes/index.js | 4 ++++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 154925d1a..f9d23f69e 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -308,6 +308,9 @@ "connectingToRinkeby": { "message": "Connecting to Rinkeby Test Network" }, + "connectingToLocalhost": { + "message": "Connecting to Localhost 8545" + }, "connectingToUnknown": { "message": "Connecting to Unknown Network" }, diff --git a/ui/app/components/app/dropdowns/network-dropdown.js b/ui/app/components/app/dropdowns/network-dropdown.js index 3d9037a06..7e645725c 100644 --- a/ui/app/components/app/dropdowns/network-dropdown.js +++ b/ui/app/components/app/dropdowns/network-dropdown.js @@ -285,6 +285,8 @@ NetworkDropdown.prototype.getNetworkName = function () { name = this.context.t('kovan') } else if (providerName === 'rinkeby') { name = this.context.t('rinkeby') + } else if (providerName === 'localhost') { + name = this.context.t('localhost') } else { name = provider.nickname || this.context.t('unknownNetwork') } diff --git a/ui/app/components/app/loading-network-screen/loading-network-screen.component.js b/ui/app/components/app/loading-network-screen/loading-network-screen.component.js index 348a997c8..b79051d0b 100644 --- a/ui/app/components/app/loading-network-screen/loading-network-screen.component.js +++ b/ui/app/components/app/loading-network-screen/loading-network-screen.component.js @@ -45,6 +45,8 @@ export default class LoadingNetworkScreen extends PureComponent { name = this.context.t('connectingToKovan') } else if (providerName === 'rinkeby') { name = this.context.t('connectingToRinkeby') + } else if (providerName === 'localhost') { + name = this.context.t('connectingToLocalhost') } else { name = this.context.t('connectingTo', [providerId]) } diff --git a/ui/app/components/app/network.js b/ui/app/components/app/network.js index e18404f42..54065dd73 100644 --- a/ui/app/components/app/network.js +++ b/ui/app/components/app/network.js @@ -139,7 +139,7 @@ Network.prototype.render = function () { }, }), - h('.network-name', providerNick || context.t('privateNetwork')), + h('.network-name', providerName === 'localhost' ? context.t('localhost') : providerNick || context.t('privateNetwork')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) } diff --git a/ui/app/pages/routes/index.js b/ui/app/pages/routes/index.js index e06d88c90..37ff2df61 100644 --- a/ui/app/pages/routes/index.js +++ b/ui/app/pages/routes/index.js @@ -267,6 +267,8 @@ class Routes extends Component { name = this.context.t('connectingToKovan') } else if (providerName === 'rinkeby') { name = this.context.t('connectingToRinkeby') + } else if (providerName === 'localhost') { + name = this.context.t('connectingToLocalhost') } else { name = this.context.t('connectingTo', [providerId]) } @@ -288,6 +290,8 @@ class Routes extends Component { name = this.context.t('kovan') } else if (providerName === 'rinkeby') { name = this.context.t('rinkeby') + } else if (providerName === 'localhost') { + name = this.context.t('localhost') } else { name = this.context.t('unknownNetwork') } -- cgit From a973a7420abed55998b88c339d1c6a1c53bd63e2 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Wed, 10 Apr 2019 18:37:15 -0400 Subject: feature: switch token pricing to CoinGecko API (#6424) --- app/scripts/controllers/token-rates.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index 867d36433..4e396bb59 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -28,16 +28,16 @@ class TokenRatesController { async updateExchangeRates () { if (!this.isActive) { return } const contractExchangeRates = {} - const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toUpperCase() : 'ETH' - const pairs = this._tokens.map(token => `pairs[]=${token.address}/${nativeCurrency}`) - const query = pairs.join('&') + const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toLowerCase() : 'eth' + const pairs = this._tokens.map(token => token.address).join(',') + const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}` if (this._tokens.length > 0) { try { - const response = await fetch(`https://exchanges.balanc3.net/pie?${query}&autoConversion=false`) - const { prices = [] } = await response.json() - prices.forEach(({ pair, price }) => { - const address = pair.split('/')[0] - contractExchangeRates[normalizeAddress(address)] = typeof price === 'number' ? price : 0 + const response = await fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`) + const prices = await response.json() + this._tokens.forEach(token => { + const price = prices[token.address.toLowerCase()] + contractExchangeRates[normalizeAddress(token.address)] = price ? price[nativeCurrency] : 0 }) } catch (error) { log.warn(`MetaMask - TokenRatesController exchange rate fetch failed.`, error) -- cgit From 7ceb1c63ccceeb631c148b9244987dba54f77b12 Mon Sep 17 00:00:00 2001 From: Etienne Dusseault Date: Thu, 11 Apr 2019 09:26:47 +0800 Subject: Added Chrome limited site access solution doc (#6422) --- docs/limited_site_access.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/limited_site_access.md diff --git a/docs/limited_site_access.md b/docs/limited_site_access.md new file mode 100644 index 000000000..f703d5c7e --- /dev/null +++ b/docs/limited_site_access.md @@ -0,0 +1,5 @@ +# Google Chrome/Brave Limited Site Access for Extensions + +Problem: MetaMask doesn't work with limited site access enabled under Chrome's extensions. + +Solution: In addition to the site you wish to whitelist, you must add 'api.infura.io' as another domain, so the MetaMask extension is authorized to make RPC calls to Infura. -- cgit From 2786932576a92f8e75ee798d91c8222741c774c9 Mon Sep 17 00:00:00 2001 From: Sneh Koul <35871990+Sneh1999@users.noreply.github.com> Date: Thu, 11 Apr 2019 10:50:03 -0700 Subject: repeated getSelectedAddress() func send.selectors.js removed (#6056) --- ui/app/components/app/send/send.container.js | 5 ++++- ui/app/components/app/send/send.selectors.js | 8 +------- ui/app/components/app/send/tests/send-container.test.js | 5 ++++- ui/app/components/app/send/tests/send-selectors.test.js | 9 --------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/ui/app/components/app/send/send.container.js b/ui/app/components/app/send/send.container.js index e65463b93..303639c76 100644 --- a/ui/app/components/app/send/send.container.js +++ b/ui/app/components/app/send/send.container.js @@ -2,6 +2,10 @@ import { connect } from 'react-redux' import SendEther from './send.component' import { withRouter } from 'react-router-dom' import { compose } from 'recompose' +const { + getSelectedAddress, +} = require('../../../selectors/selectors') + import { getAmountConversionRate, getBlockGasLimit, @@ -12,7 +16,6 @@ import { getGasTotal, getPrimaryCurrency, getRecentBlocks, - getSelectedAddress, getSelectedToken, getSelectedTokenContract, getSelectedTokenToFiatRate, diff --git a/ui/app/components/app/send/send.selectors.js b/ui/app/components/app/send/send.selectors.js index 2ec677ad1..89f047d50 100644 --- a/ui/app/components/app/send/send.selectors.js +++ b/ui/app/components/app/send/send.selectors.js @@ -5,6 +5,7 @@ const { } = require('../../../helpers/utils/conversion-util') const { getMetaMaskAccounts, + getSelectedAddress, } = require('../../../selectors/selectors') const { estimateGasPriceFromRecentBlocks, @@ -33,7 +34,6 @@ const selectors = { getPrimaryCurrency, getRecentBlocks, getSelectedAccount, - getSelectedAddress, getSelectedIdentity, getSelectedToken, getSelectedTokenContract, @@ -149,12 +149,6 @@ function getSelectedAccount (state) { return accounts[selectedAddress] } -function getSelectedAddress (state) { - const selectedAddress = state.metamask.selectedAddress || Object.keys(getMetaMaskAccounts(state))[0] - - return selectedAddress -} - function getSelectedIdentity (state) { const selectedAddress = getSelectedAddress(state) const identities = state.metamask.identities diff --git a/ui/app/components/app/send/tests/send-container.test.js b/ui/app/components/app/send/tests/send-container.test.js index 9538b67b3..9d7365ac9 100644 --- a/ui/app/components/app/send/tests/send-container.test.js +++ b/ui/app/components/app/send/tests/send-container.test.js @@ -35,7 +35,6 @@ proxyquire('../send.container.js', { getGasTotal: (s) => `mockGasTotal:${s}`, getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`, getRecentBlocks: (s) => `mockRecentBlocks:${s}`, - getSelectedAddress: (s) => `mockSelectedAddress:${s}`, getSelectedToken: (s) => `mockSelectedToken:${s}`, getSelectedTokenContract: (s) => `mockTokenContract:${s}`, getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`, @@ -47,11 +46,15 @@ proxyquire('../send.container.js', { getTokenBalance: (s) => `mockTokenBalance:${s}`, getQrCodeData: (s) => `mockQrCodeData:${s}`, }, + '../../../selectors/selectors': { + getSelectedAddress: (s) => `mockSelectedAddress:${s}`, + }, '../../../store/actions': actionSpies, '../../../ducks/send/send.duck': duckActionSpies, './send.utils.js': { calcGasTotal: (gasLimit, gasPrice) => gasLimit + gasPrice, }, + }) describe('send container', () => { diff --git a/ui/app/components/app/send/tests/send-selectors.test.js b/ui/app/components/app/send/tests/send-selectors.test.js index cdc86fe59..ccc126795 100644 --- a/ui/app/components/app/send/tests/send-selectors.test.js +++ b/ui/app/components/app/send/tests/send-selectors.test.js @@ -20,7 +20,6 @@ const { getPrimaryCurrency, getRecentBlocks, getSelectedAccount, - getSelectedAddress, getSelectedIdentity, getSelectedToken, getSelectedTokenContract, @@ -274,14 +273,6 @@ describe('send selectors', () => { }) }) - describe('getSelectedAddress()', () => { - it('should', () => { - assert.equal( - getSelectedAddress(mockState), - '0xd85a4b6a394794842887b8284293d69163007bbb' - ) - }) - }) describe('getSelectedIdentity()', () => { it('should return the identity object of the currently selected address', () => { -- cgit From c4a3d4ea82ef5488c8c91db77beca85679447722 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Thu, 11 Apr 2019 21:33:33 -0230 Subject: Remove unneeded array cloning in getSendToAccounts selector The use of `Object.entries` here to map the accounts into a new array effectively produces a shallow clone of the array without guaranteeing the order of the original array (as object iteration order is implementation-specific and variable). From MDN [1]: > The **`Object.entries()`** method returns an array of a given object's own enumerable > string-keyed property `[key, value]` pairs, in the same order as that provided by a > `for...in` loop And also: > The ordering of the properties is the same as that given by looping over the > property values of the object manually. Both of which suggest that the iteration order is the same as `for...in`, which is to say that it's not specified. [2] [3] This changeset removes the cloning, keeping the shallow clone created the line before which preserves the order of the items in the array. [1]:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries [2]:https://stackoverflow.com/a/5525820/1267663 [3]:https://stackoverflow.com/a/30919039/1267663 --- ui/app/components/app/send/send.selectors.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/app/components/app/send/send.selectors.js b/ui/app/components/app/send/send.selectors.js index 89f047d50..6056dea21 100644 --- a/ui/app/components/app/send/send.selectors.js +++ b/ui/app/components/app/send/send.selectors.js @@ -240,9 +240,7 @@ function getSendTo (state) { function getSendToAccounts (state) { const fromAccounts = accountsWithSendEtherInfoSelector(state) const addressBookAccounts = getAddressBook(state) - const allAccounts = [...fromAccounts, ...addressBookAccounts] - // TODO: figure out exactly what the below returns and put a descriptive variable name on it - return Object.entries(allAccounts).map(([key, account]) => account) + return [...fromAccounts, ...addressBookAccounts] } function getSendWarnings (state) { -- cgit From 7c38ad9356bdf87d4a1aed15ec31c8968ed49cb8 Mon Sep 17 00:00:00 2001 From: Esteban Mino Date: Fri, 12 Apr 2019 12:26:39 -0400 Subject: bump contract metadata --- package-lock.json | 97 ++++++++++++++++++++++++++++++++++++++++++++++++------- package.json | 2 +- 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11eab5718..88f1832ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9759,8 +9759,8 @@ } }, "eth-contract-metadata": { - "version": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe", - "from": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe" + "version": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f", + "from": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f" }, "eth-ens-namehash": { "version": "2.0.8", @@ -9806,6 +9806,31 @@ "requires": { "bn.js": "^4.11.8", "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" } } } @@ -9814,8 +9839,7 @@ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" + "bn.js": "^4.11.8" } }, "ethereumjs-util": { @@ -9896,6 +9920,31 @@ "requires": { "bn.js": "^4.11.8", "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" } } } @@ -9904,8 +9953,7 @@ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" + "bn.js": "^4.11.8" } }, "ethereumjs-util": { @@ -10076,6 +10124,33 @@ "requires": { "bn.js": "^4.11.8", "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", + "dev": true, + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" } } } @@ -10084,14 +10159,14 @@ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" + "bn.js": "^4.11.8" } }, "ethereumjs-util": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "dev": true, "requires": { "bn.js": "^4.11.0", "create-hash": "^1.1.2", @@ -10192,8 +10267,7 @@ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" + "bn.js": "^4.11.8" } }, "ethereumjs-util": { @@ -10502,8 +10576,7 @@ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" + "bn.js": "^4.11.8" } }, "ethereumjs-util": { diff --git a/package.json b/package.json index 884c77387..2c81507ce 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "ensnare": "^1.0.0", "eth-bin-to-ops": "^1.0.1", "eth-block-tracker": "^4.1.0", - "eth-contract-metadata": "github:MetaMask/eth-contract-metadata#92e7d1442c7585bfd24e50a0fda78df11dedadfe", + "eth-contract-metadata": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f", "eth-ens-namehash": "^2.0.8", "eth-hd-keyring": "^1.2.2", "eth-json-rpc-filters": "^3.0.1", -- cgit From 33836c04638bd0ef023ed913e4c8ec976ad3caae Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Tue, 16 Apr 2019 11:15:53 -0600 Subject: Set rpcTarget, nickname, and ticker when selecting one of the default networks --- app/scripts/controllers/network/network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 47432c1e2..ab1198dd3 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -140,10 +140,10 @@ module.exports = class NetworkController extends EventEmitter { this.providerConfig = providerConfig } - async setProviderType (type) { + async setProviderType (type, rpcTarget = '', ticker = 'ETH', nickname = '') { assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`) assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`) - const providerConfig = { type } + const providerConfig = { type, rpcTarget, ticker, nickname } this.providerConfig = providerConfig } -- cgit From fb22fb12cafec238a2143df6e94218c890e4ba4e Mon Sep 17 00:00:00 2001 From: Sneh Koul <35871990+Sneh1999@users.noreply.github.com> Date: Tue, 16 Apr 2019 10:59:11 -0700 Subject: Adds e2e test for most web3 methods that dapps use (#6160) * schema added * ui for the dapp added and schema.js changed according to the comments in PR * added tests for all web3 methods * Update run-all.sh * Update web3.spec.js to work with new onboarding flow * changes made according to the comments * Create stand alone script for web3 e2e tests. * Lint fixes for web3 e2e tests. --- package.json | 2 + test/e2e/beta/run-web3.sh | 9 ++ test/e2e/beta/web3.spec.js | 365 +++++++++++++++++++++++++++++++++++++++++++++ test/web3/index.html | 105 +++++++++++++ test/web3/schema.js | 209 ++++++++++++++++++++++++++ test/web3/web3.js | 34 +++++ 6 files changed, 724 insertions(+) create mode 100755 test/e2e/beta/run-web3.sh create mode 100644 test/e2e/beta/web3.spec.js create mode 100644 test/web3/index.html create mode 100644 test/web3/schema.js create mode 100644 test/web3/web3.js diff --git a/package.json b/package.json index 2c81507ce..c0355d12c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "test:integration:build": "gulp build:scss", "test:e2e:drizzle:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-drizzle.sh", "test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/beta/run-all.sh", + "test:web3:chrome": "SELENIUM_BROWSER=chrome test/e2e/beta/run-web3.sh", + "test:web3:firefox": "SELENIUM_BROWSER=firefox test/e2e/beta/run-web3.sh", "test:e2e:firefox": "SELENIUM_BROWSER=firefox test/e2e/beta/run-all.sh", "test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'", "test:screens:run": "node test/screens/new-ui.js", diff --git a/test/e2e/beta/run-web3.sh b/test/e2e/beta/run-web3.sh new file mode 100755 index 000000000..9f77060de --- /dev/null +++ b/test/e2e/beta/run-web3.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + +export PATH="$PATH:./node_modules/.bin" + +shell-parallel -s 'static-server test/web3 --port 8080' -x 'sleep 5 && mocha test/e2e/beta/web3.spec' \ No newline at end of file diff --git a/test/e2e/beta/web3.spec.js b/test/e2e/beta/web3.spec.js new file mode 100644 index 000000000..b3962c821 --- /dev/null +++ b/test/e2e/beta/web3.spec.js @@ -0,0 +1,365 @@ +const path = require('path') +const assert = require('assert') +const webdriver = require('selenium-webdriver') +const { By } = webdriver +const { + delay, + buildChromeWebDriver, + buildFirefoxWebdriver, + installWebExt, + getExtensionIdChrome, + getExtensionIdFirefox, +} = require('../func') +const { + checkBrowserForConsoleErrors, + closeAllWindowHandlesExcept, + findElement, + findElements, + openNewPage, + switchToWindowWithTitle, + verboseReportOnFailure, + waitUntilXWindowHandles, +} = require('./helpers') +const fetchMockResponses = require('./fetch-mocks.js') + + +describe('Using MetaMask with an existing account', function () { + let extensionId + let driver + + const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress' + const regularDelayMs = 1000 + const largeDelayMs = regularDelayMs * 2 + + const button = async (x) => { + const buttoncheck = x + await buttoncheck.click() + await delay(largeDelayMs) + const [results] = await findElements(driver, By.css('#results')) + const resulttext = await results.getText() + var parsedData = JSON.parse(resulttext) + + return (parsedData) + + } + + this.timeout(0) + this.bail(true) + + before(async function () { + let extensionUrl + switch (process.env.SELENIUM_BROWSER) { + case 'chrome': { + const extensionPath = path.resolve('dist/chrome') + driver = buildChromeWebDriver(extensionPath) + extensionId = await getExtensionIdChrome(driver) + await delay(regularDelayMs) + extensionUrl = `chrome-extension://${extensionId}/home.html` + break + } + case 'firefox': { + const extensionPath = path.resolve('dist/firefox') + driver = buildFirefoxWebdriver() + await installWebExt(driver, extensionPath) + await delay(regularDelayMs) + extensionId = await getExtensionIdFirefox(driver) + extensionUrl = `moz-extension://${extensionId}/home.html` + break + } + } + // Depending on the state of the application built into the above directory (extPath) and the value of + // METAMASK_DEBUG we will see different post-install behaviour and possibly some extra windows. Here we + // are closing any extraneous windows to reset us to a single window before continuing. + const [tab1] = await driver.getAllWindowHandles() + await closeAllWindowHandlesExcept(driver, [tab1]) + await driver.switchTo().window(tab1) + await driver.get(extensionUrl) + }) + + beforeEach(async function () { + await driver.executeScript( + 'window.origFetch = window.fetch.bind(window);' + + 'window.fetch = ' + + '(...args) => { ' + + 'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' + + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' + + '(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' + + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' + + '(args[0].match(/chromeextensionmm/)) { return ' + + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' + + '(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' + + 'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' + + 'return window.origFetch(...args); };' + + 'function cancelInfuraRequest(requestDetails) {' + + 'console.log("Canceling: " + requestDetails.url);' + + 'return {' + + 'cancel: true' + + '};' + + ' }' + + 'window.chrome && window.chrome.webRequest && window.chrome.webRequest.onBeforeRequest.addListener(' + + 'cancelInfuraRequest,' + + '{urls: ["https://*.infura.io/*"]},' + + '["blocking"]' + + ');' + ) + }) + + afterEach(async function () { + if (process.env.SELENIUM_BROWSER === 'chrome') { + const errors = await checkBrowserForConsoleErrors(driver) + if (errors.length) { + const errorReports = errors.map(err => err.message) + const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}` + console.error(new Error(errorMessage)) + } + } + if (this.currentTest.state === 'failed') { + await verboseReportOnFailure(driver, this.currentTest) + } + }) + + after(async function () { + await driver.quit() + }) + + describe('First time flow starting from an existing seed phrase', () => { + it('clicks the continue button on the welcome screen', async () => { + await findElement(driver, By.css('.welcome-page__header')) + const welcomeScreenBtn = await findElement(driver, By.css('.first-time-flow__button')) + welcomeScreenBtn.click() + await delay(largeDelayMs) + }) + + it('clicks the "Import Wallet" option', async () => { + const customRpcButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Import Wallet')]`)) + customRpcButton.click() + await delay(largeDelayMs) + }) + + it('clicks the "No thanks" option on the metametrics opt-in screen', async () => { + const optOutButton = await findElement(driver, By.css('.btn-default')) + optOutButton.click() + await delay(largeDelayMs) + }) + + it('imports a seed phrase', async () => { + const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea')) + await seedTextArea.sendKeys(testSeedPhrase) + await delay(regularDelayMs) + + const [password] = await findElements(driver, By.id('password')) + await password.sendKeys('correct horse battery staple') + const [confirmPassword] = await findElements(driver, By.id('confirm-password')) + confirmPassword.sendKeys('correct horse battery staple') + + const tosCheckBox = await findElement(driver, By.css('.first-time-flow__checkbox')) + await tosCheckBox.click() + + const [importButton] = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`)) + await importButton.click() + await delay(regularDelayMs) + }) + + it('clicks through the success screen', async () => { + await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`)) + const doneButton = await findElement(driver, By.css('button.first-time-flow__button')) + await doneButton.click() + await delay(regularDelayMs) + }) + }) + + + describe('opens dapp', () => { + + it('switches to mainnet', async () => { + const networkDropdown = await findElement(driver, By.css('.network-name')) + await networkDropdown.click() + await delay(regularDelayMs) + + const [mainnet] = await findElements(driver, By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`)) + await mainnet.click() + await delay(largeDelayMs * 2) + }) + + it('', async () => { + await openNewPage(driver, 'http://127.0.0.1:8080/') + await delay(regularDelayMs) + + await waitUntilXWindowHandles(driver, 3) + const windowHandles = await driver.getAllWindowHandles() + + const extension = windowHandles[0] + const popup = await switchToWindowWithTitle(driver, 'MetaMask Notification', windowHandles) + const dapp = windowHandles.find(handle => handle !== extension && handle !== popup) + + await delay(regularDelayMs) + const approveButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Connect')]`)) + await approveButton.click() + + await driver.switchTo().window(dapp) + await delay(regularDelayMs) + + + }) + }) + + describe('testing web3 methods', async () => { + + + it('testing hexa methods', async () => { + + + var List = await driver.findElements(By.className('hexaNumberMethods')) + + for (let i = 0; i < List.length; i++) { + try { + + var parsedData = await button(List[i]) + console.log(parsedData) + var result = parseInt(parsedData.result, 16) + + assert.equal((typeof result === 'number'), true) + await delay(regularDelayMs) + } catch (err) { + console.log(err) + assert(false) + + } + } + }) + + it('testing booleanMethods', async () => { + + var List = await driver.findElements(By.className('booleanMethods')) + + for (let i = 0; i < List.length; i++) { + try { + + var parsedData = await button(List[i]) + console.log(parsedData) + var result = parsedData.result + + assert.equal(result, false) + await delay(regularDelayMs) + } catch (err) { + console.log(err) + assert(false) + + + } + } + + }) + + it('testing transactionMethods', async () => { + + var List = await driver.findElements(By.className('transactionMethods')) + + for (let i = 0; i < List.length; i++) { + try { + + var parsedData = await button(List[i]) + + console.log(parsedData.result.blockHash) + + var result = [] + result.push(parseInt(parsedData.result.blockHash, 16)) + result.push(parseInt(parsedData.result.blockNumber, 16)) + result.push(parseInt(parsedData.result.gas, 16)) + result.push(parseInt(parsedData.result.gasPrice, 16)) + result.push(parseInt(parsedData.result.hash, 16)) + result.push(parseInt(parsedData.result.input, 16)) + result.push(parseInt(parsedData.result.nonce, 16)) + result.push(parseInt(parsedData.result.r, 16)) + result.push(parseInt(parsedData.result.s, 16)) + result.push(parseInt(parsedData.result.v, 16)) + result.push(parseInt(parsedData.result.to, 16)) + result.push(parseInt(parsedData.result.value, 16)) + + + result.forEach((value) => { + assert.equal((typeof value === 'number'), true) + }) + + + } catch (err) { + + console.log(err) + assert(false) + + + } + } + + }) + + it('testing blockMethods', async () => { + + var List = await driver.findElements(By.className('blockMethods')) + + for (let i = 0; i < List.length; i++) { + try { + + var parsedData = await button(List[i]) + console.log(JSON.stringify(parsedData) + i) + + console.log(parsedData.result.parentHash) + + var result = parseInt(parsedData.result.parentHash, 16) + + assert.equal((typeof result === 'number'), true) + await delay(regularDelayMs) + } catch (err) { + + console.log(err) + assert(false) + + + } + } + }) + + it('testing methods', async () => { + + var List = await driver.findElements(By.className('methods')) + var parsedData + var result + + for (let i = 0; i < List.length; i++) { + try { + + if (i === 2) { + + parsedData = await button(List[i]) + console.log(parsedData.result.blockHash) + + result = parseInt(parsedData.result.blockHash, 16) + + assert.equal((typeof result === 'number' || (result === 0)), true) + await delay(regularDelayMs) + } else { + parsedData = await button(List[i]) + console.log(parsedData.result) + + result = parseInt(parsedData.result, 16) + + assert.equal((typeof result === 'number' || (result === 0)), true) + await delay(regularDelayMs) + } + + + } catch (err) { + + console.log(err) + assert(false) + + + } + } + }) + + + }) + + + }) diff --git a/test/web3/index.html b/test/web3/index.html new file mode 100644 index 000000000..cbc43290c --- /dev/null +++ b/test/web3/index.html @@ -0,0 +1,105 @@ + + + Web3 Test Dapp + + +
+
hexaNumberMethods
+
+ + + + + + + +
+
+ + + +
+
+ + + + + + +
+
+
+
booleanMethods
+
+ + + +
+
+
+
transactionMethods
+
+ + + + + +
+
+ +
+
blockMethods
+ +
+ + + + +
+
+ + + +
+
+ +
+
Methods
+
+ + + + +
+
+
+
+
+ + + + + + + + + + diff --git a/test/web3/schema.js b/test/web3/schema.js new file mode 100644 index 000000000..61977f140 --- /dev/null +++ b/test/web3/schema.js @@ -0,0 +1,209 @@ +/* eslint no-unused-vars: 0 */ + +var params = { + // diffrent params used in the methods + param: [], + blockHashParams: '0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35', + filterParams: ['0xfe704947a3cd3ca12541458a4321c869'], + transactionHashParams: [ + '0xbb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0', + ], + blockHashAndIndexParams: [ + '0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35', + '0x0', + ], + uncleByBlockNumberAndIndexParams: ['0x29c', '0x0'], + blockParameterParams: '0x5bad55', + data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', + addressParams: '0xc94770007dda54cF92009BFF0dE90c06F603a09f', + getStorageAtParams: [ + '0x295a70b2de5e3953354a6a8344e616ed314d7251', + '0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9', + '0x65a8db', + ], + getCodeParams: ['0x06012c8cf97bead5deae237070f9587f8e7a266d', '0x65a8db'], + estimateTransaction: { + from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', + to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', + gas: '0x76c0', + gasPrice: '0x9184e72a000', + value: '0x9184e72a', + data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675', + }, + filterGetLogs: [{'blockHash': '0x7c5a35e9cb3e8ae0e221ab470abae9d446c3a5626ce6689fc777dcffcab52c70', 'topics': ['0x241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80']}], + block: { + __required: [], + number: 'Q', + hash: 'D32', + parentHash: 'D32', + nonce: 'D', + sha3Uncles: 'D', + logsBloom: 'D', + transactionsRoot: 'D', + stateRoot: 'D', + receiptsRoot: 'D', + miner: 'D', + difficulty: 'Q', + totalDifficulty: 'Q', + extraData: 'D', + size: 'Q', + gasLimit: 'Q', + gasUsed: 'Q', + timestamp: 'Q', + transactions: ['DATA|Transaction'], + uncles: ['D'], + }, + transaction: { + __required: [], + hash: 'D32', + nonce: 'Q', + blockHash: 'D32', + blockNumber: 'Q', + transactionIndex: 'Q', + from: 'D20', + to: 'D20', + value: 'Q', + gasPrice: 'Q', + gas: 'Q', + input: 'D', + }, + receipt: { + __required: [], + transactionHash: 'D32', + transactionIndex: 'Q', + blockHash: 'D32', + blockNumber: 'Q', + cumulativeGasUsed: 'Q', + gasUsed: 'Q', + contractAddress: 'D20', + logs: ['FilterChange'], + }, + + filterChange: { + __required: [], + removed: 'B', + logIndex: 'Q', + transactionIndex: 'Q', + transactionHash: 'D32', + blockHash: 'D32', + blockNumber: 'Q', + address: 'D20', + data: 'Array|DATA', + topics: ['D'], + }, +} + +var methods = { + hexaNumberMethods: { + // these are the methods which have output in the form of hexa decimal numbers + eth_blockNumber: ['eth_blockNumber', params.param, 'Q'], + eth_gasPrice: ['eth_gasPrice', params.param, 'Q'], + eth_newBlockFilter: ['eth_newBlockFilter', params.param, 'Q'], + eth_newPendingTransactionFilter: [ + 'eth_newPendingTransactionFilter', + params.param, + 'Q', + ], + eth_getUncleCountByBlockHash: [ + 'eth_getUncleCountByBlockHash', + [params.blockHashParams], + 'Q', + 1, + ], + eth_getBlockTransactionCountByHash: [ + 'eth_getBlockTransactionCountByHash', + [params.blockHashParams], + 'Q', + 1, + ], + eth_getTransactionCount: [ + 'eth_getTransactionCount', + [params.addressParams, params.blockParameterParams], + 'Q', + 1, + 2, + ], + eth_getBalance: ['eth_getBalance', [params.addressParams, 'latest'], 'Q', 1, 2], + eth_estimateGas: ['eth_estimateGas', [params.estimateTransaction], 'Q', 1], + eth_getUncleCountByBlockNumber: [ + 'eth_getUncleCountByBlockNumber', + [params.blockParameterParams], + 'Q', + 1, + ], + eth_getBlockTransactionCountByNumber: [ + 'eth_getBlockTransactionCountByNumber', + ['latest'], + 'Q', + 1, + ], + eth_protocolVersion: ['eth_protocolVersion', params.param, 'S'], + eth_getCode: ['eth_getCode', params.getCodeParams, 'D', 1, 2], + }, + booleanMethods: { + // these are the methods which have output in the form of boolean + eth_uninstallFilter: ['eth_uninstallFilter', params.filterParams, 'B', 1], + eth_mining: ['eth_mining', params.param, 'B'], + eth_syncing: ['eth_syncing', params.param, 'B|EthSyncing'], + }, + transactionMethods: { + // these are the methods which have output in the form of transaction object + eth_getTransactionByHash: [ + 'eth_getTransactionByHash', + params.transactionHashParams, + params.transaction, + 1, + ], + eth_getTransactionByBlockHashAndIndex: [ + 'eth_getTransactionByBlockHashAndIndex', + params.blockHashAndIndexParams, + params.transaction, + 2, + ], + eth_getTransactionByBlockNumberAndIndex: [ + 'eth_getTransactionByBlockNumberAndIndex', + [params.blockParameterParams, '0x0'], + params.transaction, + 2, + ], + + }, + blockMethods: { + // these are the methods which have output in the form of a block + + eth_getUncleByBlockNumberAndIndex: [ + 'eth_getUncleByBlockNumberAndIndex', + params.uncleByBlockNumberAndIndexParams, + params.block, + 2, + ], + eth_getBlockByHash: [ + 'eth_getBlockByHash', + [params.params, false], + params.block, + 2, + ], + eth_getBlockByNumber: [ + 'eth_getBlockByNumber', + [params.blockParameterParams, false], + params.block, + 2, + ], + }, + + methods: { + // these are the methods which have output in the form of bytes data + + eth_call: ['eth_call', [params.estimateTransaction, 'latest'], 'D', 1, 2], + eth_getStorageAt: ['eth_getStorageAt', params.getStorageAtParams, 'D', 2, 2], + eth_getTransactionReceipt: [ + 'eth_getTransactionReceipt', + params.transactionHashParams, + params.receipt, + 1, + ], + + }, + +} + diff --git a/test/web3/web3.js b/test/web3/web3.js new file mode 100644 index 000000000..5c2de078d --- /dev/null +++ b/test/web3/web3.js @@ -0,0 +1,34 @@ +/* eslint no-undef: 0 */ + +var json = methods + +web3.currentProvider.enable().then(() => { + + Object.keys(json).forEach(methodGroupKey => { + + console.log(methodGroupKey) + const methodGroup = json[methodGroupKey] + console.log(methodGroup) + Object.keys(methodGroup).forEach(methodKey => { + + const methodButton = document.getElementById(methodKey) + methodButton.addEventListener('click', function (event) { + + window.ethereum.sendAsync({ + method: methodKey, + params: methodGroup[methodKey][1], + }, function (err, result) { + if (err) { + console.log(err) + console.log(methodKey) + } else { + document.getElementById('results').innerHTML = JSON.stringify(result) + } + }) + }) + + }) + + }) + }) + -- cgit From 92c03bdff2281b5901151ad0840b83e40dad73bc Mon Sep 17 00:00:00 2001 From: Chi Kei Chan Date: Tue, 16 Apr 2019 12:35:22 -0700 Subject: Update buttons & colors to match design system (#6446) * Refactoring button styles * renaming buttons * Add Link and Button styles * Update new btn styles and storybook * Fix tests * Change font weight; Update storybook * Fix linter --- test/integration/lib/confirm-sig-requests.js | 6 +- test/integration/lib/send-new-ui.js | 6 +- ui/app/components/app/add-token-button/index.scss | 5 +- ui/app/components/app/coinbase-form.js | 69 ------ ui/app/components/app/customize-gas-modal/index.js | 2 +- ui/app/components/app/modal/modal.component.js | 2 +- .../app/modal/tests/modal.component.test.js | 4 +- .../components/app/modals/account-details-modal.js | 4 +- .../customize-gas/customize-gas.component.js | 2 +- .../components/app/modals/deposit-ether-modal.js | 2 +- .../app/modals/edit-account-name-modal.js | 2 +- .../app/modals/export-private-key-modal.js | 4 +- .../app/modals/hide-token-confirmation-modal.js | 4 +- ui/app/components/app/modals/notification-modal.js | 4 +- ui/app/components/app/shapeshift-form.js | 2 +- ui/app/components/app/signature-request.js | 2 +- .../transaction-view-balance.component.js | 4 +- ui/app/components/app/wallet-view.js | 2 +- ui/app/components/ui/button/button.component.js | 4 + ui/app/components/ui/button/button.stories.js | 47 ++-- ui/app/components/ui/button/buttons.scss | 244 +++++++++++++++++++++ ui/app/components/ui/page-container/index.scss | 8 - .../page-container-footer.component.js | 2 +- ui/app/css/itcss/components/buttons.scss | 230 ------------------- ui/app/css/itcss/components/index.scss | 2 +- ui/app/css/itcss/components/modal.scss | 2 + ui/app/css/itcss/generic/index.scss | 1 + ui/app/css/itcss/settings/typography.scss | 37 ++++ ui/app/css/itcss/settings/variables.scss | 18 +- .../token-list/token-list-placeholder/index.scss | 3 +- .../confirm-add-suggested-token.component.js | 2 +- .../confirm-add-token.component.js | 2 +- .../connect-hardware/account-list.js | 2 +- .../connect-hardware/connect-screen.js | 2 +- ui/app/pages/create-account/import-account/json.js | 2 +- .../create-account/import-account/private-key.js | 2 +- ui/app/pages/create-account/new-account.js | 2 +- .../import-with-seed-phrase.component.js | 2 +- .../new-account/new-account.component.js | 2 +- .../unique-image/unique-image.component.js | 2 +- .../end-of-flow/end-of-flow.component.js | 2 +- .../confirm-seed-phrase.component.js | 2 +- .../reveal-seed-phrase.component.js | 2 +- .../select-action/select-action.component.js | 2 +- .../first-time-flow/welcome/welcome.component.js | 2 +- ui/app/pages/keychains/reveal-seed.js | 2 +- ui/app/pages/mobile-sync/index.js | 2 +- .../advanced-tab/advanced-tab.component.js | 6 +- ui/app/pages/settings/info-tab/index.scss | 2 +- .../security-tab/security-tab.component.js | 6 +- ui/app/pages/settings/settings-tab/index.scss | 20 +- 51 files changed, 391 insertions(+), 401 deletions(-) delete mode 100644 ui/app/components/app/coinbase-form.js create mode 100644 ui/app/components/ui/button/buttons.scss delete mode 100644 ui/app/css/itcss/components/buttons.scss diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js index e4540c4f2..c3b0dfcff 100644 --- a/test/integration/lib/confirm-sig-requests.js +++ b/test/integration/lib/confirm-sig-requests.js @@ -44,7 +44,7 @@ async function runConfirmSigRequestsTest (assert, done) { let confirmSigRowValue = await queryAsync($, '.request-signature__row-value') assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0') - let confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large') + let confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large') confirmSigSignButton[0].click() await timeout(1000) confirmSigHeadline = await queryAsync($, '.request-signature__headline') @@ -53,7 +53,7 @@ async function runConfirmSigRequestsTest (assert, done) { confirmSigRowValue = await queryAsync($, '.request-signature__row-value') assert.ok(confirmSigRowValue[0].textContent.match(/^#\sTerms\sof\sUse/)) - confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large') + confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large') confirmSigSignButton[0].click() await timeout(1000) confirmSigHeadline = await queryAsync($, '.request-signature__headline') @@ -63,7 +63,7 @@ async function runConfirmSigRequestsTest (assert, done) { assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!') assert.equal(confirmSigRowValue[1].textContent, '1337') - confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large') + confirmSigSignButton = await queryAsync($, 'button.btn-secondary.btn--large') confirmSigSignButton[0].click() await timeout(2000) diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js index d7003f4cc..ce470fc02 100644 --- a/test/integration/lib/send-new-ui.js +++ b/test/integration/lib/send-new-ui.js @@ -43,7 +43,7 @@ async function runSendFlowTest (assert, done) { selectState.val('send new ui') reactTriggerChange(selectState[0]) - const sendScreenButton = await queryAsync($, 'button.btn-primary.transaction-view-balance__button') + const sendScreenButton = await queryAsync($, 'button.btn-secondary.transaction-view-balance__button') assert.ok(sendScreenButton[1], 'send screen button present') sendScreenButton[1].click() @@ -88,7 +88,7 @@ async function runSendFlowTest (assert, done) { errorMessage = $('.send-v2__error') assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected') - const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button') + const sendButton = await queryAsync($, 'button.btn-secondary.btn--large.page-container__footer-button') assert.equal(sendButton[0].textContent, 'Next', 'next button rendered') sendButton[0].click() await timeout() @@ -122,7 +122,7 @@ async function runSendFlowTest (assert, done) { sendAmountFieldInputInEdit.val('1.0') reactTriggerChange(sendAmountFieldInputInEdit[0]) - const sendButtonInEdit = await queryAsync($, '.btn-primary.btn--large.page-container__footer-button') + const sendButtonInEdit = await queryAsync($, '.btn-secondary.btn--large.page-container__footer-button') assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered') selectState.val('send new ui') diff --git a/ui/app/components/app/add-token-button/index.scss b/ui/app/components/app/add-token-button/index.scss index 39f404716..c4350a2d3 100644 --- a/ui/app/components/app/add-token-button/index.scss +++ b/ui/app/components/app/add-token-button/index.scss @@ -17,10 +17,7 @@ } &__button { - font-size: 0.75rem; + @extend %small-link; margin: 1rem; - text-transform: uppercase; - color: $curious-blue; - cursor: pointer; } } diff --git a/ui/app/components/app/coinbase-form.js b/ui/app/components/app/coinbase-form.js deleted file mode 100644 index 24d287604..000000000 --- a/ui/app/components/app/coinbase-form.js +++ /dev/null @@ -1,69 +0,0 @@ -const Component = require('react').Component -const PropTypes = require('prop-types') -const h = require('react-hyperscript') -const inherits = require('util').inherits -const connect = require('react-redux').connect -const actions = require('../../store/actions') - -CoinbaseForm.contextTypes = { - t: PropTypes.func, -} - -module.exports = connect(mapStateToProps)(CoinbaseForm) - - -function mapStateToProps (state) { - return { - warning: state.appState.warning, - } -} - -inherits(CoinbaseForm, Component) - -function CoinbaseForm () { - Component.call(this) -} - -CoinbaseForm.prototype.render = function () { - var props = this.props - - return h('.flex-column', { - style: { - marginTop: '35px', - padding: '25px', - width: '100%', - }, - }, [ - h('.flex-row', { - style: { - justifyContent: 'space-around', - margin: '33px', - marginTop: '0px', - }, - }, [ - h('button.btn-green', { - onClick: this.toCoinbase.bind(this), - }, this.context.t('continueToCoinbase')), - - h('button.btn-red', { - onClick: () => props.dispatch(actions.goHome()), - }, this.context.t('cancel')), - ]), - ]) -} - -CoinbaseForm.prototype.toCoinbase = function () { - const props = this.props - const address = props.buyView.buyAddress - props.dispatch(actions.buyEth({ network: '1', address, amount: 0 })) -} - -CoinbaseForm.prototype.renderLoading = function () { - return h('img', { - style: { - width: '27px', - marginRight: '-27px', - }, - src: 'images/loading.svg', - }) -} diff --git a/ui/app/components/app/customize-gas-modal/index.js b/ui/app/components/app/customize-gas-modal/index.js index dca77bb00..4434b8c25 100644 --- a/ui/app/components/app/customize-gas-modal/index.js +++ b/ui/app/components/app/customize-gas-modal/index.js @@ -382,7 +382,7 @@ CustomizeGasModal.prototype.render = function () { onClick: this.props.hideModal, }, [this.context.t('cancel')]), h(Button, { - type: 'primary', + type: 'secondary', className: 'send-v2__customize-gas__save', onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal), disabled: error, diff --git a/ui/app/components/app/modal/modal.component.js b/ui/app/components/app/modal/modal.component.js index 49e131b3c..44b180ac8 100644 --- a/ui/app/components/app/modal/modal.component.js +++ b/ui/app/components/app/modal/modal.component.js @@ -20,7 +20,7 @@ export default class Modal extends PureComponent { } static defaultProps = { - submitType: 'primary', + submitType: 'secondary', cancelType: 'default', } diff --git a/ui/app/components/app/modal/tests/modal.component.test.js b/ui/app/components/app/modal/tests/modal.component.test.js index a13d7c06a..5922177a6 100644 --- a/ui/app/components/app/modal/tests/modal.component.test.js +++ b/ui/app/components/app/modal/tests/modal.component.test.js @@ -12,7 +12,7 @@ describe('Modal Component', () => { assert.equal(wrapper.find('.modal-container').length, 1) const buttons = wrapper.find(Button) assert.equal(buttons.length, 1) - assert.equal(buttons.at(0).props().type, 'primary') + assert.equal(buttons.at(0).props().type, 'secondary') }) it('should render a modal with a cancel and a submit button', () => { @@ -38,7 +38,7 @@ describe('Modal Component', () => { cancelButton.simulate('click') assert.equal(handleCancel.callCount, 1) - assert.equal(submitButton.props().type, 'primary') + assert.equal(submitButton.props().type, 'secondary') assert.equal(submitButton.props().children, 'Submit') assert.equal(handleSubmit.callCount, 0) submitButton.simulate('click') diff --git a/ui/app/components/app/modals/account-details-modal.js b/ui/app/components/app/modals/account-details-modal.js index 94ed04df9..1b1ca6b8e 100644 --- a/ui/app/components/app/modals/account-details-modal.js +++ b/ui/app/components/app/modals/account-details-modal.js @@ -84,7 +84,7 @@ AccountDetailsModal.prototype.render = function () { h('div.account-modal-divider'), h(Button, { - type: 'primary', + type: 'secondary', className: 'account-modal__button', onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }), }, this.context.t('etherscanView')), @@ -92,7 +92,7 @@ AccountDetailsModal.prototype.render = function () { // Holding on redesign for Export Private Key functionality exportPrivateKeyFeatureEnabled ? h(Button, { - type: 'primary', + type: 'secondary', className: 'account-modal__button', onClick: () => showExportPrivateKeyModal(), }, this.context.t('exportPrivateKey')) : null, diff --git a/ui/app/components/app/modals/customize-gas/customize-gas.component.js b/ui/app/components/app/modals/customize-gas/customize-gas.component.js index 5db5c79e7..178f45729 100644 --- a/ui/app/components/app/modals/customize-gas/customize-gas.component.js +++ b/ui/app/components/app/modals/customize-gas/customize-gas.component.js @@ -128,7 +128,7 @@ export default class CustomizeGas extends Component { { t('cancel') } ) - .add('secondary', () => + .add('Button - Secondary', () => ) - .add('default', () => ( + .add('Button - Default', () => - )) - .add('large primary', () => ( + ) + .add('Button - Warning', () => - )) - .add('large secondary', () => ( + ) + .add('Button - Danger', () => - )) - .add('large default', () => ( + ) + .add('Button - Danger Primary', () => - )) + ) + .add('Button - Link', () => + + ) diff --git a/ui/app/components/ui/button/buttons.scss b/ui/app/components/ui/button/buttons.scss new file mode 100644 index 000000000..0fc87415b --- /dev/null +++ b/ui/app/components/ui/button/buttons.scss @@ -0,0 +1,244 @@ +/* + Buttons + */ + +$hover-secondary: #B0D7F2; +$hover-default: #B3B3B3; +$hover-confirm: #0372C3; +$hover-red: #FEB6BF; +$hover-red-primary: #C72837; +$hover-orange: #FFD3B5; + +%button { + @include h6; + + font-weight: 500; + font-family: Roboto, Arial; + line-height: 1.25rem; + padding: .75rem 1rem; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + border-radius: 6px; + width: 100%; + outline: none; + transition: border-color .3s ease, background-color .3s ease; + + &--disabled, + &[disabled] { + cursor: auto; + opacity: .5; + pointer-events: none; + } +} + +%link { + @include h4; + + color: $Blue-500; + line-height: 1.25rem; + cursor: pointer; + background-color: transparent; + + &:hover { + color: $Blue-400; + } + + &:active { + color: $Blue-600; + } + + &--disabled, + &[disabled] { + cursor: auto; + opacity: 1; + pointer-events: none; + color: $hover-secondary; + } +} + +%small-link { + @extend %link; + @include h6; +} + +.button { + @extend %button; +} + +.btn-secondary { + color: $Blue-500; + border: 2px solid $hover-secondary; + + &:hover { + border-color: $Blue-500; + } + + &:active { + background: $Blue-000; + border-color: $Blue-500; + } + + &--disabled, + &[disabled] { + opacity: 1; + color: $hover-secondary; + } +} + +.btn-warning { + color: $Orange-500; + border: 2px solid $hover-orange; + + &:hover { + border-color: $Orange-500; + } + + &:active { + background: $Orange-000; + border-color: $Orange-500; + } + + &--disabled, + &[disabled] { + opacity: 1; + color: $hover-orange; + } +} + +.btn-danger { + color: $Red-500; + border: 2px solid $hover-red; + + &:hover { + border-color: $Red-500; + } + + &:active { + background: $Red-000; + border-color: $Red-500; + } + + &--disabled, + &[disabled] { + opacity: 1; + color: $hover-red; + } +} + +.btn-danger-primary { + color: $white; + border: 2px solid $Red-500; + background-color: $Red-500; + + &:hover { + border-color: $hover-red-primary; + background-color: $hover-red-primary; + } + + &:active { + background: $Red-600; + border-color: $Red-600; + } + + &--disabled, + &[disabled] { + opacity: 1; + border-color: $hover-red; + background-color: $hover-red; + } +} + +.btn-default { + color: $Grey-500; + border: 2px solid $hover-default; + + &:hover { + border-color: $Grey-500; + } + + &:active { + background: #FBFBFC; + border-color: $Grey-500; + } + + &--disabled, + &[disabled] { + opacity: 1; + color: $hover-default; + } +} + +.btn-primary { + color: $white; + border: 2px solid $Blue-500; + background-color: $Blue-500; + + &:hover { + border-color: $hover-confirm; + background-color: $hover-confirm; + } + + &:active { + background: $Blue-600; + border-color: $Blue-600; + } + + &--disabled, + &[disabled] { + border-color: $hover-secondary; + background-color: $hover-secondary; + } +} + +.btn-link { + @extend %link; +} + +.btn--large { + min-height: 54px; +} + +/** + All Buttons styles are deviations from design guide + */ + +.btn-raised { + color: $curious-blue; + background-color: $white; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08); + padding: 6px; + height: initial; + min-height: initial; + width: initial; + min-width: initial; +} + +.btn--first-time { + height: 54px; + width: 198px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14); + color: $white; + font-size: 1.25rem; + font-weight: 500; + transition: 200ms ease-in-out; + background-color: rgba(247, 134, 28, .9); + border-radius: 0; +} + +button[disabled], +input[type="submit"][disabled] { + cursor: not-allowed; + opacity: .5; +} + +button.primary { + padding: 8px 12px; + background: #f7861c; + box-shadow: 0 3px 6px rgba(247, 134, 28, .36); + color: $white; + font-size: 1.1em; + font-family: Roboto; + text-transform: uppercase; +} diff --git a/ui/app/components/ui/page-container/index.scss b/ui/app/components/ui/page-container/index.scss index b71a3cb9d..003c5a0e2 100644 --- a/ui/app/components/ui/page-container/index.scss +++ b/ui/app/components/ui/page-container/index.scss @@ -55,11 +55,6 @@ border-top: 1px solid $geyser; flex: 0 0 auto; - .btn-default, - .btn-confirm { - font-size: 1rem; - } - header { display: flex; flex-flow: row; @@ -86,9 +81,6 @@ } &__footer-button { - height: 55px; - font-size: 1rem; - text-transform: uppercase; margin-right: 16px; &:last-of-type { diff --git a/ui/app/components/ui/page-container/page-container-footer/page-container-footer.component.js b/ui/app/components/ui/page-container/page-container-footer/page-container-footer.component.js index 85b16cefe..4ef203521 100644 --- a/ui/app/components/ui/page-container/page-container-footer/page-container-footer.component.js +++ b/ui/app/components/ui/page-container/page-container-footer/page-container-footer.component.js @@ -45,7 +45,7 @@ export default class PageContainerFooter extends Component { } - - ) - } -} diff --git a/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/index.js b/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/index.js deleted file mode 100644 index dba0edb7b..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './gas-fee-display.component' diff --git a/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js deleted file mode 100644 index cb4180508..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' -import assert from 'assert' -import {shallow} from 'enzyme' -import GasFeeDisplay from '../gas-fee-display.component' -import UserPreferencedCurrencyDisplay from '../../../../../user-preferenced-currency-display' -import sinon from 'sinon' - - -const propsMethodSpies = { - showCustomizeGasModal: sinon.spy(), - onReset: sinon.spy(), -} - -describe('GasFeeDisplay Component', function () { - let wrapper - - beforeEach(() => { - wrapper = shallow(, {context: {t: str => str + '_t'}}) - }) - - afterEach(() => { - propsMethodSpies.showCustomizeGasModal.resetHistory() - }) - - describe('render', () => { - it('should render a CurrencyDisplay component', () => { - assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2) - }) - - it('should render the CurrencyDisplay with the correct props', () => { - const { - type, - value, - } = wrapper.find(UserPreferencedCurrencyDisplay).at(0).props() - assert.equal(type, 'PRIMARY') - assert.equal(value, 'mockGasTotal') - }) - - it('should render the reset button with the correct props', () => { - const { - onClick, - className, - } = wrapper.find('button').props() - assert.equal(className, 'gas-fee-reset') - assert.equal(propsMethodSpies.onReset.callCount, 0) - onClick() - assert.equal(propsMethodSpies.onReset.callCount, 1) - }) - - it('should render the reset button with the correct text', () => { - assert.equal(wrapper.find('button').text(), 'reset_t') - }) - }) -}) diff --git a/ui/app/components/app/send/send-content/send-gas-row/index.js b/ui/app/components/app/send/send-content/send-gas-row/index.js deleted file mode 100644 index 3c7ff1d5f..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './send-gas-row.container' diff --git a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.component.js deleted file mode 100644 index 424a65b20..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.component.js +++ /dev/null @@ -1,131 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import SendRowWrapper from '../send-row-wrapper' -import GasFeeDisplay from './gas-fee-display/gas-fee-display.component' -import GasPriceButtonGroup from '../../../gas-customization/gas-price-button-group' -import AdvancedGasInputs from '../../../gas-customization/advanced-gas-inputs' - -export default class SendGasRow extends Component { - - static propTypes = { - conversionRate: PropTypes.number, - convertedCurrency: PropTypes.string, - gasFeeError: PropTypes.bool, - gasLoadingError: PropTypes.bool, - gasTotal: PropTypes.string, - showCustomizeGasModal: PropTypes.func, - setGasPrice: PropTypes.func, - setGasLimit: PropTypes.func, - gasPriceButtonGroupProps: PropTypes.object, - gasButtonGroupShown: PropTypes.bool, - advancedInlineGasShown: PropTypes.bool, - resetGasButtons: PropTypes.func, - gasPrice: PropTypes.number, - gasLimit: PropTypes.number, - insufficientBalance: PropTypes.bool, - } - - static contextTypes = { - t: PropTypes.func, - metricsEvent: PropTypes.func, - }; - - renderAdvancedOptionsButton () { - const { metricsEvent } = this.context - const { showCustomizeGasModal } = this.props - return
{ - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Edit Screen', - name: 'Clicked "Advanced Options"', - }, - }) - showCustomizeGasModal() - }}> - { this.context.t('advancedOptions') } -
- } - - renderContent () { - const { - conversionRate, - convertedCurrency, - gasLoadingError, - gasTotal, - showCustomizeGasModal, - gasPriceButtonGroupProps, - gasButtonGroupShown, - advancedInlineGasShown, - resetGasButtons, - setGasPrice, - setGasLimit, - gasPrice, - gasLimit, - insufficientBalance, - } = this.props - const { metricsEvent } = this.context - - const gasPriceButtonGroup =
- { - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Edit Screen', - name: 'Changed Gas Button', - }, - }) - gasPriceButtonGroupProps.handleGasPriceSelection(...args) - }} - /> - { this.renderAdvancedOptionsButton() } -
- const gasFeeDisplay = showCustomizeGasModal()} - /> - const advancedGasInputs =
- setGasPrice(newGasPrice, gasLimit)} - updateCustomGasLimit={newGasLimit => setGasLimit(newGasLimit, gasPrice)} - customGasPrice={gasPrice} - customGasLimit={gasLimit} - insufficientBalance={insufficientBalance} - customPriceIsSafe={true} - isSpeedUp={false} - /> - { this.renderAdvancedOptionsButton() } -
- - if (advancedInlineGasShown) { - return advancedGasInputs - } else if (gasButtonGroupShown) { - return gasPriceButtonGroup - } else { - return gasFeeDisplay - } - } - - render () { - const { gasFeeError } = this.props - - return ( - - { this.renderContent() } - - ) - } - -} diff --git a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.container.js deleted file mode 100644 index f81670c02..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.container.js +++ /dev/null @@ -1,118 +0,0 @@ -import { connect } from 'react-redux' -import { - getConversionRate, - getCurrentCurrency, - getGasTotal, - getGasPrice, - getGasLimit, - getSendAmount, -} from '../../send.selectors.js' -import { - isBalanceSufficient, - calcGasTotal, -} from '../../send.utils.js' -import { - getBasicGasEstimateLoadingStatus, - getRenderableEstimateDataForSmallButtonsFromGWEI, - getDefaultActiveButtonIndex, -} from '../../../../../selectors/custom-gas' -import { - showGasButtonGroup, -} from '../../../../../ducks/send/send.duck' -import { - resetCustomData, - setCustomGasPrice, - setCustomGasLimit, -} from '../../../../../ducks/gas/gas.duck' -import { getGasLoadingError, gasFeeIsInError, getGasButtonGroupShown } from './send-gas-row.selectors.js' -import { showModal, setGasPrice, setGasLimit, setGasTotal } from '../../../../../store/actions' -import { getAdvancedInlineGasShown, getCurrentEthBalance, getSelectedToken } from '../../../../../selectors/selectors' -import SendGasRow from './send-gas-row.component' - -export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow) - -function mapStateToProps (state) { - const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state) - const gasPrice = getGasPrice(state) - const gasLimit = getGasLimit(state) - const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, gasPrice) - - const gasTotal = getGasTotal(state) - const conversionRate = getConversionRate(state) - const balance = getCurrentEthBalance(state) - - const insufficientBalance = !isBalanceSufficient({ - amount: getSelectedToken(state) ? '0x0' : getSendAmount(state), - gasTotal, - balance, - conversionRate, - }) - - return { - conversionRate, - convertedCurrency: getCurrentCurrency(state), - gasTotal, - gasFeeError: gasFeeIsInError(state), - gasLoadingError: getGasLoadingError(state), - gasPriceButtonGroupProps: { - buttonDataLoading: getBasicGasEstimateLoadingStatus(state), - defaultActiveButtonIndex: 1, - newActiveButtonIndex: activeButtonIndex > -1 ? activeButtonIndex : null, - gasButtonInfo, - }, - gasButtonGroupShown: getGasButtonGroupShown(state), - advancedInlineGasShown: getAdvancedInlineGasShown(state), - gasPrice, - gasLimit, - insufficientBalance, - } -} - -function mapDispatchToProps (dispatch) { - return { - showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS', hideBasic: true })), - setGasPrice: (newPrice, gasLimit) => { - dispatch(setGasPrice(newPrice)) - dispatch(setCustomGasPrice(newPrice)) - if (gasLimit) { - dispatch(setGasTotal(calcGasTotal(gasLimit, newPrice))) - } - }, - setGasLimit: (newLimit, gasPrice) => { - dispatch(setGasLimit(newLimit)) - dispatch(setCustomGasLimit(newLimit)) - if (gasPrice) { - dispatch(setGasTotal(calcGasTotal(newLimit, gasPrice))) - } - }, - showGasButtonGroup: () => dispatch(showGasButtonGroup()), - resetCustomData: () => dispatch(resetCustomData()), - } -} - -function mergeProps (stateProps, dispatchProps, ownProps) { - const { gasPriceButtonGroupProps } = stateProps - const { gasButtonInfo } = gasPriceButtonGroupProps - const { - setGasPrice: dispatchSetGasPrice, - showGasButtonGroup: dispatchShowGasButtonGroup, - resetCustomData: dispatchResetCustomData, - ...otherDispatchProps - } = dispatchProps - - return { - ...stateProps, - ...otherDispatchProps, - ...ownProps, - gasPriceButtonGroupProps: { - ...gasPriceButtonGroupProps, - handleGasPriceSelection: dispatchSetGasPrice, - }, - resetGasButtons: () => { - dispatchResetCustomData() - dispatchSetGasPrice(gasButtonInfo[1].priceInHexWei) - dispatchShowGasButtonGroup() - }, - setGasPrice: dispatchSetGasPrice, - } -} diff --git a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.scss b/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.selectors.js b/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.selectors.js deleted file mode 100644 index 79c838543..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/send-gas-row.selectors.js +++ /dev/null @@ -1,19 +0,0 @@ -const selectors = { - gasFeeIsInError, - getGasLoadingError, - getGasButtonGroupShown, -} - -module.exports = selectors - -function getGasLoadingError (state) { - return state.send.errors.gasLoading -} - -function gasFeeIsInError (state) { - return Boolean(state.send.errors.gasFee) -} - -function getGasButtonGroupShown (state) { - return state.send.gasButtonGroupShown -} diff --git a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-component.test.js deleted file mode 100644 index 08f26854e..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-component.test.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react' -import assert from 'assert' -import { shallow } from 'enzyme' -import sinon from 'sinon' -import SendGasRow from '../send-gas-row.component.js' - -import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component' -import GasFeeDisplay from '../gas-fee-display/gas-fee-display.component' -import GasPriceButtonGroup from '../../../../gas-customization/gas-price-button-group' - -const propsMethodSpies = { - showCustomizeGasModal: sinon.spy(), - resetGasButtons: sinon.spy(), -} - -describe('SendGasRow Component', function () { - let wrapper - - beforeEach(() => { - wrapper = shallow(, { context: { t: str => str + '_t', metricsEvent: () => ({}) } }) - }) - - afterEach(() => { - propsMethodSpies.resetGasButtons.resetHistory() - }) - - describe('render', () => { - it('should render a SendRowWrapper component', () => { - assert.equal(wrapper.find(SendRowWrapper).length, 1) - }) - - it('should pass the correct props to SendRowWrapper', () => { - const { - label, - showError, - errorType, - } = wrapper.find(SendRowWrapper).props() - - assert.equal(label, 'transactionFee_t:') - assert.equal(showError, 'mockGasFeeError') - assert.equal(errorType, 'gasFee') - }) - - it('should render a GasFeeDisplay as a child of the SendRowWrapper', () => { - assert(wrapper.find(SendRowWrapper).childAt(0).is(GasFeeDisplay)) - }) - - it('should render the GasFeeDisplay with the correct props', () => { - const { - conversionRate, - convertedCurrency, - gasLoadingError, - gasTotal, - onReset, - } = wrapper.find(SendRowWrapper).childAt(0).props() - assert.equal(conversionRate, 20) - assert.equal(convertedCurrency, 'mockConvertedCurrency') - assert.equal(gasLoadingError, false) - assert.equal(gasTotal, 'mockGasTotal') - assert.equal(propsMethodSpies.resetGasButtons.callCount, 0) - onReset() - assert.equal(propsMethodSpies.resetGasButtons.callCount, 1) - }) - - it('should render the GasPriceButtonGroup if gasButtonGroupShown is true', () => { - wrapper.setProps({ gasButtonGroupShown: true }) - const rendered = wrapper.find(SendRowWrapper).childAt(0) - assert.equal(rendered.children().length, 2) - - const gasPriceButtonGroup = rendered.childAt(0) - assert(gasPriceButtonGroup.is(GasPriceButtonGroup)) - assert(gasPriceButtonGroup.hasClass('gas-price-button-group--small')) - assert.equal(gasPriceButtonGroup.props().showCheck, false) - assert.equal(gasPriceButtonGroup.props().someGasPriceButtonGroupProp, 'foo') - assert.equal(gasPriceButtonGroup.props().anotherGasPriceButtonGroupProp, 'bar') - }) - - it('should render an advanced options button if gasButtonGroupShown is true', () => { - wrapper.setProps({ gasButtonGroupShown: true }) - const rendered = wrapper.find(SendRowWrapper).childAt(0) - assert.equal(rendered.children().length, 2) - - const advancedOptionsButton = rendered.childAt(1) - assert.equal(advancedOptionsButton.text(), 'advancedOptions_t') - - assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0) - advancedOptionsButton.props().onClick() - assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1) - }) - }) -}) diff --git a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-container.test.js deleted file mode 100644 index d1f753639..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-container.test.js +++ /dev/null @@ -1,200 +0,0 @@ -import assert from 'assert' -import proxyquire from 'proxyquire' -import sinon from 'sinon' - -let mapStateToProps -let mapDispatchToProps -let mergeProps - -const actionSpies = { - showModal: sinon.spy(), - setGasPrice: sinon.spy(), - setGasTotal: sinon.spy(), - setGasLimit: sinon.spy(), -} - -const sendDuckSpies = { - showGasButtonGroup: sinon.spy(), -} - -const gasDuckSpies = { - resetCustomData: sinon.spy(), - setCustomGasPrice: sinon.spy(), - setCustomGasLimit: sinon.spy(), -} - -proxyquire('../send-gas-row.container.js', { - 'react-redux': { - connect: (ms, md, mp) => { - mapStateToProps = ms - mapDispatchToProps = md - mergeProps = mp - return () => ({}) - }, - }, - '../../../../../selectors/selectors': { - getCurrentEthBalance: (s) => `mockCurrentEthBalance:${s}`, - getAdvancedInlineGasShown: (s) => `mockAdvancedInlineGasShown:${s}`, - getSelectedToken: () => false, - }, - '../../send.selectors.js': { - getConversionRate: (s) => `mockConversionRate:${s}`, - getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`, - getGasTotal: (s) => `mockGasTotal:${s}`, - getGasPrice: (s) => `mockGasPrice:${s}`, - getGasLimit: (s) => `mockGasLimit:${s}`, - getSendAmount: (s) => `mockSendAmount:${s}`, - }, - '../../send.utils.js': { - isBalanceSufficient: ({ - amount, - gasTotal, - balance, - conversionRate, - }) => `${amount}:${gasTotal}:${balance}:${conversionRate}`, - calcGasTotal: (gasLimit, gasPrice) => gasLimit + gasPrice, - }, - './send-gas-row.selectors.js': { - getGasLoadingError: (s) => `mockGasLoadingError:${s}`, - gasFeeIsInError: (s) => `mockGasFeeError:${s}`, - getGasButtonGroupShown: (s) => `mockGetGasButtonGroupShown:${s}`, - }, - '../../../../../store/actions': actionSpies, - '../../../../../selectors/custom-gas': { - getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${s}`, - getRenderableEstimateDataForSmallButtonsFromGWEI: (s) => `mockGasButtonInfo:${s}`, - getDefaultActiveButtonIndex: (gasButtonInfo, gasPrice) => gasButtonInfo.length + gasPrice.length, - }, - '../../../../../ducks/send/send.duck': sendDuckSpies, - '../../../../../ducks/gas/gas.duck': gasDuckSpies, -}) - -describe('send-gas-row container', () => { - - describe('mapStateToProps()', () => { - - it('should map the correct properties to props', () => { - assert.deepEqual(mapStateToProps('mockState'), { - conversionRate: 'mockConversionRate:mockState', - convertedCurrency: 'mockConvertedCurrency:mockState', - gasTotal: 'mockGasTotal:mockState', - gasFeeError: 'mockGasFeeError:mockState', - gasLoadingError: 'mockGasLoadingError:mockState', - gasPriceButtonGroupProps: { - buttonDataLoading: `mockBasicGasEstimateLoadingStatus:mockState`, - defaultActiveButtonIndex: 1, - newActiveButtonIndex: 49, - gasButtonInfo: `mockGasButtonInfo:mockState`, - }, - gasButtonGroupShown: `mockGetGasButtonGroupShown:mockState`, - advancedInlineGasShown: 'mockAdvancedInlineGasShown:mockState', - gasLimit: 'mockGasLimit:mockState', - gasPrice: 'mockGasPrice:mockState', - insufficientBalance: false, - }) - }) - - }) - - describe('mapDispatchToProps()', () => { - let dispatchSpy - let mapDispatchToPropsObject - - beforeEach(() => { - dispatchSpy = sinon.spy() - mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) - actionSpies.setGasTotal.resetHistory() - }) - - describe('showCustomizeGasModal()', () => { - it('should dispatch an action', () => { - mapDispatchToPropsObject.showCustomizeGasModal() - assert(dispatchSpy.calledOnce) - assert.deepEqual( - actionSpies.showModal.getCall(0).args[0], - { name: 'CUSTOMIZE_GAS', hideBasic: true } - ) - }) - }) - - describe('setGasPrice()', () => { - it('should dispatch an action', () => { - mapDispatchToPropsObject.setGasPrice('mockNewPrice', 'mockLimit') - assert(dispatchSpy.calledThrice) - assert(actionSpies.setGasPrice.calledOnce) - assert.equal(actionSpies.setGasPrice.getCall(0).args[0], 'mockNewPrice') - assert.equal(gasDuckSpies.setCustomGasPrice.getCall(0).args[0], 'mockNewPrice') - assert(actionSpies.setGasTotal.calledOnce) - assert.equal(actionSpies.setGasTotal.getCall(0).args[0], 'mockLimitmockNewPrice') - }) - }) - - describe('setGasLimit()', () => { - it('should dispatch an action', () => { - mapDispatchToPropsObject.setGasLimit('mockNewLimit', 'mockPrice') - assert(dispatchSpy.calledThrice) - assert(actionSpies.setGasLimit.calledOnce) - assert.equal(actionSpies.setGasLimit.getCall(0).args[0], 'mockNewLimit') - assert.equal(gasDuckSpies.setCustomGasLimit.getCall(0).args[0], 'mockNewLimit') - assert(actionSpies.setGasTotal.calledOnce) - assert.equal(actionSpies.setGasTotal.getCall(0).args[0], 'mockNewLimitmockPrice') - }) - }) - - describe('showGasButtonGroup()', () => { - it('should dispatch an action', () => { - mapDispatchToPropsObject.showGasButtonGroup() - assert(dispatchSpy.calledOnce) - assert(sendDuckSpies.showGasButtonGroup.calledOnce) - }) - }) - - describe('resetCustomData()', () => { - it('should dispatch an action', () => { - mapDispatchToPropsObject.resetCustomData() - assert(dispatchSpy.calledOnce) - assert(gasDuckSpies.resetCustomData.calledOnce) - }) - }) - - }) - - describe('mergeProps', () => { - let stateProps - let dispatchProps - let ownProps - - beforeEach(() => { - stateProps = { - gasPriceButtonGroupProps: { - someGasPriceButtonGroupProp: 'foo', - anotherGasPriceButtonGroupProp: 'bar', - }, - someOtherStateProp: 'baz', - } - dispatchProps = { - setGasPrice: sinon.spy(), - someOtherDispatchProp: sinon.spy(), - } - ownProps = { someOwnProp: 123 } - }) - - it('should return the expected props when isConfirm is true', () => { - const result = mergeProps(stateProps, dispatchProps, ownProps) - - assert.equal(result.someOtherStateProp, 'baz') - assert.equal(result.gasPriceButtonGroupProps.someGasPriceButtonGroupProp, 'foo') - assert.equal(result.gasPriceButtonGroupProps.anotherGasPriceButtonGroupProp, 'bar') - assert.equal(result.someOwnProp, 123) - - assert.equal(dispatchProps.setGasPrice.callCount, 0) - result.gasPriceButtonGroupProps.handleGasPriceSelection() - assert.equal(dispatchProps.setGasPrice.callCount, 1) - - assert.equal(dispatchProps.someOtherDispatchProp.callCount, 0) - result.someOtherDispatchProp() - assert.equal(dispatchProps.someOtherDispatchProp.callCount, 1) - }) - }) - -}) diff --git a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js b/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js deleted file mode 100644 index bd3c9a257..000000000 --- a/ui/app/components/app/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import assert from 'assert' -import { - gasFeeIsInError, - getGasLoadingError, - getGasButtonGroupShown, -} from '../send-gas-row.selectors.js' - -describe('send-gas-row selectors', () => { - - describe('getGasLoadingError()', () => { - it('should return send.errors.gasLoading', () => { - const state = { - send: { - errors: { - gasLoading: 'abc', - }, - }, - } - - assert.equal(getGasLoadingError(state), 'abc') - }) - }) - - describe('gasFeeIsInError()', () => { - it('should return true if send.errors.gasFee is truthy', () => { - const state = { - send: { - errors: { - gasFee: 'def', - }, - }, - } - - assert.equal(gasFeeIsInError(state), true) - }) - - it('should return false send.errors.gasFee is falsely', () => { - const state = { - send: { - errors: { - gasFee: null, - }, - }, - } - - assert.equal(gasFeeIsInError(state), false) - }) - }) - - describe('getGasButtonGroupShown()', () => { - it('should return send.gasButtonGroupShown', () => { - const state = { - send: { - gasButtonGroupShown: 'foobar', - }, - } - - assert.equal(getGasButtonGroupShown(state), 'foobar') - }) - }) - -}) diff --git a/ui/app/components/app/send/send-content/send-hex-data-row/index.js b/ui/app/components/app/send/send-content/send-hex-data-row/index.js deleted file mode 100644 index 08c341067..000000000 --- a/ui/app/components/app/send/send-content/send-hex-data-row/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './send-hex-data-row.container' diff --git a/ui/app/components/app/send/send-content/send-hex-data-row/send-hex-data-row.component.js b/ui/app/components/app/send/send-content/send-hex-data-row/send-hex-data-row.component.js deleted file mode 100644 index 62a74a77b..000000000 --- a/ui/app/components/app/send/send-content/send-hex-data-row/send-hex-data-row.component.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import SendRowWrapper from '../send-row-wrapper' - -export default class SendHexDataRow extends Component { - static propTypes = { - data: PropTypes.string, - inError: PropTypes.bool, - updateSendHexData: PropTypes.func.isRequired, - updateGas: PropTypes.func.isRequired, - }; - - static contextTypes = { - t: PropTypes.func, - }; - - onInput = (event) => { - const {updateSendHexData, updateGas} = this.props - const data = event.target.value.replace(/\n/g, '') || null - updateSendHexData(data) - updateGas({ data }) - } - - render () { - const {inError} = this.props - const {t} = this.context - - return ( - -