mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'develop' into TokensPerAccountBasis
This commit is contained in:
commit
228f48c6e5
6
.github/CODEOWNERS
vendored
Normal file
6
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Lines starting with '#' are comments.
|
||||||
|
# Each line is a file pattern followed by one or more owners.
|
||||||
|
|
||||||
|
ui/ @danjm @alextsg @whymarrh
|
||||||
|
app/scripts/controllers/transactions @frankiebee
|
||||||
|
|
@ -17,6 +17,6 @@
|
|||||||
{ "code": "tml", "name": "Tamil" },
|
{ "code": "tml", "name": "Tamil" },
|
||||||
{ "code": "tr", "name": "Turkish" },
|
{ "code": "tr", "name": "Turkish" },
|
||||||
{ "code": "vi", "name": "Vietnamese" },
|
{ "code": "vi", "name": "Vietnamese" },
|
||||||
{ "code": "zh_CN", "name": "Mandarin" },
|
{ "code": "zh_CN", "name": "Chinese (Simplified)" },
|
||||||
{ "code": "zh_TW", "name": "Taiwanese" }
|
{ "code": "zh_TW", "name": "Chinese (Traditional)" }
|
||||||
]
|
]
|
||||||
|
@ -122,6 +122,9 @@
|
|||||||
"copy": {
|
"copy": {
|
||||||
"message": "コピー"
|
"message": "コピー"
|
||||||
},
|
},
|
||||||
|
"copyContractAddress": {
|
||||||
|
"message": "コントラクトアドレスをコピー"
|
||||||
|
},
|
||||||
"copyToClipboard": {
|
"copyToClipboard": {
|
||||||
"message": "クリップボードへコピー"
|
"message": "クリップボードへコピー"
|
||||||
},
|
},
|
||||||
@ -395,6 +398,9 @@
|
|||||||
"mainnet": {
|
"mainnet": {
|
||||||
"message": "Ethereumメインネットワーク"
|
"message": "Ethereumメインネットワーク"
|
||||||
},
|
},
|
||||||
|
"menu": {
|
||||||
|
"message": "メニュー"
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"message": "メッセージ"
|
"message": "メッセージ"
|
||||||
},
|
},
|
||||||
@ -464,6 +470,9 @@
|
|||||||
"oldUIMessage": {
|
"oldUIMessage": {
|
||||||
"message": "旧UIを表示しています。右上のドロップダウンメニューのオプションより、新UIへ切り替えが可能です。"
|
"message": "旧UIを表示しています。右上のドロップダウンメニューのオプションより、新UIへ切り替えが可能です。"
|
||||||
},
|
},
|
||||||
|
"openInTab": {
|
||||||
|
"message": "タブを開く"
|
||||||
|
},
|
||||||
"or": {
|
"or": {
|
||||||
"message": "または",
|
"message": "または",
|
||||||
"description": "choice between creating or importing a new account"
|
"description": "choice between creating or importing a new account"
|
||||||
@ -573,6 +582,15 @@
|
|||||||
"searchResults": {
|
"searchResults": {
|
||||||
"message": "検索結果"
|
"message": "検索結果"
|
||||||
},
|
},
|
||||||
|
"newPassword8Chars": {
|
||||||
|
"message": "新しいパスワード (8桁以上)"
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"message": "選択"
|
||||||
|
},
|
||||||
|
"selectCurrency": {
|
||||||
|
"message": "通貨を選択"
|
||||||
|
},
|
||||||
"selectService": {
|
"selectService": {
|
||||||
"message": "サービスを選択"
|
"message": "サービスを選択"
|
||||||
},
|
},
|
||||||
@ -586,10 +604,14 @@
|
|||||||
"message": "ETHの送信"
|
"message": "ETHの送信"
|
||||||
},
|
},
|
||||||
"sendTokens": {
|
"sendTokens": {
|
||||||
"message": "トークンを送る"
|
"message": "トークンを送信"
|
||||||
},
|
},
|
||||||
"onlySendToEtherAddress": {
|
"onlySendToEtherAddress": {
|
||||||
"message": "ETHはイーサリウムアカウントのみに送信できます。"
|
"message": "ETH はイーサリウムアカウントのみに送信できます。"
|
||||||
|
},
|
||||||
|
"onlySendTokensToAccountAddress": {
|
||||||
|
"message": "$1 はイーサリアムアカウントのみに送信できます。",
|
||||||
|
"description": "displays token symbol"
|
||||||
},
|
},
|
||||||
"searchTokens": {
|
"searchTokens": {
|
||||||
"message": "トークンの検索"
|
"message": "トークンの検索"
|
||||||
@ -690,10 +712,10 @@
|
|||||||
"message": "パスワードの入力"
|
"message": "パスワードの入力"
|
||||||
},
|
},
|
||||||
"uiWelcome": {
|
"uiWelcome": {
|
||||||
"message": "新UIへようこそ!(ベータ版)"
|
"message": "新UIへようこそ! (ベータ版)"
|
||||||
},
|
},
|
||||||
"uiWelcomeMessage": {
|
"uiWelcomeMessage": {
|
||||||
"message": "現在Metamaskの新しいUIをお使いになっています。トークン送信など、新たな機能を試してみましょう!何か問題があればご報告ください。"
|
"message": "現在、MetaMask の新しいUIをお使いになっています。トークン送信など、新たな機能を試してみましょう! 何か問題があればご報告ください。"
|
||||||
},
|
},
|
||||||
"unavailable": {
|
"unavailable": {
|
||||||
"message": "有効ではありません。"
|
"message": "有効ではありません。"
|
||||||
@ -720,6 +742,9 @@
|
|||||||
"viewAccount": {
|
"viewAccount": {
|
||||||
"message": "アカウントを見る"
|
"message": "アカウントを見る"
|
||||||
},
|
},
|
||||||
|
"viewOnEtherscan": {
|
||||||
|
"message": "Etherscan で見る"
|
||||||
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"message": "警告"
|
"message": "警告"
|
||||||
},
|
},
|
||||||
|
BIN
app/images/ethereum-metamask-chrome.png
Normal file
BIN
app/images/ethereum-metamask-chrome.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
@ -67,7 +67,8 @@
|
|||||||
"notifications"
|
"notifications"
|
||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
"inpage.js"
|
"inpage.js",
|
||||||
|
"phishing.html"
|
||||||
],
|
],
|
||||||
"externally_connectable": {
|
"externally_connectable": {
|
||||||
"matches": [
|
"matches": [
|
||||||
|
60
app/phishing.html
Normal file
60
app/phishing.html
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Phishing Warning</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #c50000;
|
||||||
|
padding: 50px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
.centered {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||||
|
ga('create', 'UA-37075177-6', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
//Send referral data to EAL
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||||
|
ga('create', 'UA-68598031-1', 'auto' {'allowLinker':true});
|
||||||
|
ga('send', 'pageview');
|
||||||
|
ga('require', 'linker');
|
||||||
|
ga('linker:autoLink', ['harrydenley.com', 'metamask.io'], false, true);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="centered">
|
||||||
|
|
||||||
|
<img src="/images/ethereum-metamask-chrome.png" style="width:100%">
|
||||||
|
<h3>ATTENTION</h3>
|
||||||
|
<p>MetaMask believes this domain to have malicious intent and has prevented you from interacting with it.</p>
|
||||||
|
<p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>.</p>
|
||||||
|
<p>You can turn MetaMask off to interact with this site, but it's advised not to.</p>
|
||||||
|
<p>If you think this domain is incorrectly flagged, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -178,6 +178,7 @@ function blacklistedDomainCheck () {
|
|||||||
'adyen.com',
|
'adyen.com',
|
||||||
'gravityforms.com',
|
'gravityforms.com',
|
||||||
'harbourair.com',
|
'harbourair.com',
|
||||||
|
'ani.gamer.com.tw',
|
||||||
'blueskybooking.com',
|
'blueskybooking.com',
|
||||||
]
|
]
|
||||||
var currentUrl = window.location.href
|
var currentUrl = window.location.href
|
||||||
@ -196,6 +197,7 @@ function blacklistedDomainCheck () {
|
|||||||
* Redirects the current page to a phishing information page
|
* Redirects the current page to a phishing information page
|
||||||
*/
|
*/
|
||||||
function redirectToPhishingWarning () {
|
function redirectToPhishingWarning () {
|
||||||
console.log('MetaMask - redirecting to phishing warning')
|
console.log('MetaMask - routing to Phishing Warning component')
|
||||||
window.location.href = 'https://metamask.io/phishing.html'
|
let extensionURL = extension.runtime.getURL('phishing.html')
|
||||||
|
window.location.href = extensionURL
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,10 @@ class TxGasUtil {
|
|||||||
try {
|
try {
|
||||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const simulationFailed = (
|
txMeta.simulationFails = {
|
||||||
err.message.includes('Transaction execution error.') ||
|
reason: err.message,
|
||||||
err.message.includes('gas required exceeds allowance or always failing transaction')
|
|
||||||
)
|
|
||||||
if (simulationFailed) {
|
|
||||||
txMeta.simulationFails = true
|
|
||||||
return txMeta
|
|
||||||
}
|
}
|
||||||
|
return txMeta
|
||||||
}
|
}
|
||||||
this.setTxGas(txMeta, block.gasLimit, estimatedGasHex)
|
this.setTxGas(txMeta, block.gasLimit, estimatedGasHex)
|
||||||
return txMeta
|
return txMeta
|
||||||
|
@ -37,5 +37,6 @@ module.exports = [
|
|||||||
require('./024'),
|
require('./024'),
|
||||||
require('./025'),
|
require('./025'),
|
||||||
require('./026'),
|
require('./026'),
|
||||||
|
require('./027'),
|
||||||
require('./028'),
|
require('./028'),
|
||||||
]
|
]
|
||||||
|
@ -36,14 +36,23 @@ TransactionListItem.prototype.showRetryButton = function () {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentTxIsLatest = false
|
||||||
const currentNonce = txParams.nonce
|
const currentNonce = txParams.nonce
|
||||||
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
|
const currentSubmittedTxs = transactions.filter(tx => tx.status === 'submitted')
|
||||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
|
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
|
||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
||||||
|
if (currentSubmittedTxs.length > 0) {
|
||||||
|
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||||
|
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||||
|
return tx2
|
||||||
|
})
|
||||||
|
currentTxIsLatest = lastTx.id === transaction.id
|
||||||
|
}
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
|
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000 && currentTxIsLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionListItem.prototype.render = function () {
|
TransactionListItem.prototype.render = function () {
|
||||||
|
61
ui/app/components/button-group/button-group.component.js
Normal file
61
ui/app/components/button-group/button-group.component.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import React, { PureComponent } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
export default class ButtonGroup extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
defaultActiveButtonIndex: PropTypes.number,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
children: PropTypes.array,
|
||||||
|
className: PropTypes.string,
|
||||||
|
style: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
className: 'button-group',
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
activeButtonIndex: this.props.defaultActiveButtonIndex || 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleButtonClick (activeButtonIndex) {
|
||||||
|
this.setState({ activeButtonIndex })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtons () {
|
||||||
|
const { children, disabled } = this.props
|
||||||
|
|
||||||
|
return React.Children.map(children, (child, index) => {
|
||||||
|
return child && (
|
||||||
|
<button
|
||||||
|
className={classnames(
|
||||||
|
'button-group__button',
|
||||||
|
{ 'button-group__button--active': index === this.state.activeButtonIndex },
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
this.handleButtonClick(index)
|
||||||
|
child.props.onClick && child.props.onClick()
|
||||||
|
}}
|
||||||
|
disabled={disabled || child.props.disabled}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
{ child.props.children }
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { className, style } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={className}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{ this.renderButtons() }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
49
ui/app/components/button-group/button-group.stories.js
Normal file
49
ui/app/components/button-group/button-group.stories.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { storiesOf } from '@storybook/react'
|
||||||
|
import { action } from '@storybook/addon-actions'
|
||||||
|
import ButtonGroup from './'
|
||||||
|
import Button from '../button'
|
||||||
|
import { text, boolean } from '@storybook/addon-knobs/react'
|
||||||
|
|
||||||
|
storiesOf('ButtonGroup', module)
|
||||||
|
.add('with Buttons', () =>
|
||||||
|
<ButtonGroup
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
disabled={boolean('Disabled', false)}
|
||||||
|
defaultActiveButtonIndex={1}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={action('cheap')}
|
||||||
|
>
|
||||||
|
{text('Button1', 'Cheap')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={action('average')}
|
||||||
|
>
|
||||||
|
{text('Button2', 'Average')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={action('fast')}
|
||||||
|
>
|
||||||
|
{text('Button3', 'Fast')}
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
)
|
||||||
|
.add('with a disabled Button', () =>
|
||||||
|
<ButtonGroup
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
disabled={boolean('Disabled', false)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={action('enabled')}
|
||||||
|
>
|
||||||
|
{text('Button1', 'Enabled')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={action('disabled')}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{text('Button2', 'Disabled')}
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
)
|
1
ui/app/components/button-group/index.js
Normal file
1
ui/app/components/button-group/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './button-group.component'
|
38
ui/app/components/button-group/index.scss
Normal file
38
ui/app/components/button-group/index.scss
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $tundora;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: $alto;
|
||||||
|
border-width: 1px 1px 1px;
|
||||||
|
border-left: 0;
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-left: 1px solid $alto;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background-color: $dodger-blue;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import ButtonGroup from '../button-group.component.js'
|
||||||
|
|
||||||
|
const childButtonSpies = {
|
||||||
|
onClick: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
sinon.spy(ButtonGroup.prototype, 'handleButtonClick')
|
||||||
|
sinon.spy(ButtonGroup.prototype, 'renderButtons')
|
||||||
|
|
||||||
|
const mockButtons = [
|
||||||
|
<button onClick={childButtonSpies.onClick} key={'a'}><div className="mockClass" /></button>,
|
||||||
|
<button onClick={childButtonSpies.onClick} key={'b'}></button>,
|
||||||
|
<button onClick={childButtonSpies.onClick} key={'c'}></button>,
|
||||||
|
]
|
||||||
|
|
||||||
|
describe('ButtonGroup Component', function () {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = shallow(<ButtonGroup
|
||||||
|
defaultActiveButtonIndex={1}
|
||||||
|
disabled={false}
|
||||||
|
className="someClassName"
|
||||||
|
style={ { color: 'red' } }
|
||||||
|
>{mockButtons}</ButtonGroup>)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
childButtonSpies.onClick.resetHistory()
|
||||||
|
ButtonGroup.prototype.handleButtonClick.resetHistory()
|
||||||
|
ButtonGroup.prototype.renderButtons.resetHistory()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('handleButtonClick', () => {
|
||||||
|
it('should set the activeButtonIndex', () => {
|
||||||
|
assert.equal(wrapper.state('activeButtonIndex'), 1)
|
||||||
|
wrapper.instance().handleButtonClick(2)
|
||||||
|
assert.equal(wrapper.state('activeButtonIndex'), 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('renderButtons', () => {
|
||||||
|
it('should render a button for each child', () => {
|
||||||
|
const childButtons = wrapper.find('.button-group__button')
|
||||||
|
assert.equal(childButtons.length, 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the correct button with an active state', () => {
|
||||||
|
const childButtons = wrapper.find('.button-group__button')
|
||||||
|
const activeChildButton = wrapper.find('.button-group__button--active')
|
||||||
|
assert.deepEqual(childButtons.get(1), activeChildButton.get(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call handleButtonClick and the respective button\'s onClick method when a button is clicked', () => {
|
||||||
|
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 0)
|
||||||
|
assert.equal(childButtonSpies.onClick.callCount, 0)
|
||||||
|
const childButtons = wrapper.find('.button-group__button')
|
||||||
|
childButtons.at(0).props().onClick()
|
||||||
|
childButtons.at(1).props().onClick()
|
||||||
|
childButtons.at(2).props().onClick()
|
||||||
|
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 3)
|
||||||
|
assert.equal(childButtonSpies.onClick.callCount, 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render all child buttons as disabled if props.disabled is true', () => {
|
||||||
|
const childButtons = wrapper.find('.button-group__button')
|
||||||
|
childButtons.forEach(button => {
|
||||||
|
assert.equal(button.props().disabled, undefined)
|
||||||
|
})
|
||||||
|
wrapper.setProps({ disabled: true })
|
||||||
|
const disabledChildButtons = wrapper.find('[disabled=true]')
|
||||||
|
assert.equal(disabledChildButtons.length, 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the children of the button', () => {
|
||||||
|
const mockClass = wrapper.find('.mockClass')
|
||||||
|
assert.equal(mockClass.length, 1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('render', () => {
|
||||||
|
it('should render a div with the expected class and style', () => {
|
||||||
|
assert.equal(wrapper.find('div').at(0).props().className, 'someClassName')
|
||||||
|
assert.deepEqual(wrapper.find('div').at(0).props().style, { color: 'red' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call renderButtons when rendering', () => {
|
||||||
|
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 1)
|
||||||
|
wrapper.instance().render()
|
||||||
|
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import assert from 'assert'
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
import ConfirmDetailRow from '../confirm-detail-row.component.js'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
const propsMethodSpies = {
|
||||||
|
onHeaderClick: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Confirm Detail Row Component', function () {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = shallow(<ConfirmDetailRow
|
||||||
|
errorType={'mockErrorType'}
|
||||||
|
label={'mockLabel'}
|
||||||
|
showError={false}
|
||||||
|
fiatText = {'mockFiatText'}
|
||||||
|
ethText = {'mockEthText'}
|
||||||
|
fiatTextColor= {'mockColor'}
|
||||||
|
onHeaderClick= {propsMethodSpies.onHeaderClick}
|
||||||
|
headerText = {'mockHeaderText'}
|
||||||
|
headerTextClassName = {'mockHeaderClass'}
|
||||||
|
/>)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('render', () => {
|
||||||
|
it('should render a div with a confirm-detail-row class', () => {
|
||||||
|
assert.equal(wrapper.find('div.confirm-detail-row').length, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the label as a child of the confirm-detail-row__label', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row > .confirm-detail-row__label').childAt(0).text(), 'mockLabel')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the headerText as a child of the confirm-detail-row__header-text', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__header-text').childAt(0).text(), 'mockHeaderText')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the fiatText as a child of the confirm-detail-row__fiat', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__fiat').childAt(0).text(), 'mockFiatText')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render the ethText as a child of the confirm-detail-row__eth', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__eth').childAt(0).text(), 'mockEthText')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set the fiatTextColor on confirm-detail-row__fiat', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row__fiat').props().style.color, 'mockColor')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should assure the confirm-detail-row__header-text classname is correct', () => {
|
||||||
|
assert.equal(wrapper.find('.confirm-detail-row__header-text').props().className, 'confirm-detail-row__header-text mockHeaderClass')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call onHeaderClick when headerText div gets clicked', () => {
|
||||||
|
wrapper.find('.confirm-detail-row__header-text').props().onClick()
|
||||||
|
assert.equal(assert.equal(propsMethodSpies.onHeaderClick.callCount, 1))
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
@ -1,3 +1,5 @@
|
|||||||
|
@import './button-group/index';
|
||||||
|
|
||||||
@import './export-text-container/index';
|
@import './export-text-container/index';
|
||||||
|
|
||||||
@import './selected-account/index';
|
@import './selected-account/index';
|
||||||
|
@ -213,14 +213,23 @@ TxListItem.prototype.showRetryButton = function () {
|
|||||||
if (!txParams) {
|
if (!txParams) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
let currentTxIsLatest = false
|
||||||
const currentNonce = txParams.nonce
|
const currentNonce = txParams.nonce
|
||||||
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
|
const currentSubmittedTxs = selectedAddressTxList.filter(tx => tx.status === 'submitted')
|
||||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
|
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
|
||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
lastSubmittedTxWithCurrentNonce.id === transactionId
|
lastSubmittedTxWithCurrentNonce.id === transactionId
|
||||||
|
if (currentSubmittedTxs.length > 0) {
|
||||||
|
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||||
|
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||||
|
return tx2
|
||||||
|
})
|
||||||
|
currentTxIsLatest = lastTx.id === transactionId
|
||||||
|
}
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
|
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000 && currentTxIsLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
|
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user