From 084158f1a2af9d117c054420e895f4ae76a94df0 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Fri, 31 Aug 2018 12:34:22 -0700 Subject: [PATCH] Add tests for TransactionActivityLog. Make changes to rendering events --- app/_locales/en/messages.json | 24 ++ .../transaction-activity-log/index.js | 2 +- .../transaction-activity-log/index.scss | 6 + ...transaction-activity-log.component.test.js | 35 +++ ...transaction-activity-log.container.test.js | 27 +++ .../transaction-activity-log.util.test.js | 208 ++++++++++++++++++ .../transaction-activity-log.component.js | 43 ++-- .../transaction-activity-log.container.js | 11 + .../transaction-activity-log.util.js | 32 +-- ui/app/conversion-util.js | 3 + 10 files changed, 349 insertions(+), 42 deletions(-) create mode 100644 ui/app/components/transaction-activity-log/tests/transaction-activity-log.component.test.js create mode 100644 ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js create mode 100644 ui/app/components/transaction-activity-log/tests/transaction-activity-log.util.test.js create mode 100644 ui/app/components/transaction-activity-log/transaction-activity-log.container.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index ad276306f..dc5f5dc58 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -860,6 +860,9 @@ "save": { "message": "Save" }, + "speedUp": { + "message": "speed up" + }, "speedUpTitle": { "message": "Speed Up Transaction" }, @@ -1088,6 +1091,24 @@ "total": { "message": "Total" }, + "transaction": { + "message": "transaction" + }, + "transactionConfirmed": { + "message": "Transaction confirmed." + }, + "transactionCreated": { + "message": "Transaction created with a value of $1." + }, + "transactionDropped": { + "message": "Transaction dropped." + }, + "transactionSubmitted": { + "message": "Transaction submitted." + }, + "transactionUpdatedGas": { + "message": "Transaction updated with a gas price of $1." + }, "transactions": { "message": "transactions" }, @@ -1134,6 +1155,9 @@ "unavailable": { "message": "Unavailable" }, + "units": { + "message": "units" + }, "unknown": { "message": "Unknown" }, diff --git a/ui/app/components/transaction-activity-log/index.js b/ui/app/components/transaction-activity-log/index.js index f39f8098c..a33da15a3 100644 --- a/ui/app/components/transaction-activity-log/index.js +++ b/ui/app/components/transaction-activity-log/index.js @@ -1 +1 @@ -export { default } from './transaction-activity-log.component' +export { default } from './transaction-activity-log.container' diff --git a/ui/app/components/transaction-activity-log/index.scss b/ui/app/components/transaction-activity-log/index.scss index fb24b77e2..d86514440 100644 --- a/ui/app/components/transaction-activity-log/index.scss +++ b/ui/app/components/transaction-activity-log/index.scss @@ -1,6 +1,7 @@ .transaction-activity-log { &__card { background: $white; + height: 100%; } &__activities-container { @@ -47,6 +48,11 @@ font-size: .75rem; } + &__value { + display: inline; + font-weight: 500; + } + b { font-weight: 500; } diff --git a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.component.test.js b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.component.test.js new file mode 100644 index 000000000..8687dbbc7 --- /dev/null +++ b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.component.test.js @@ -0,0 +1,35 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import TransactionActivityLog from '../transaction-activity-log.component' +import Card from '../../card' + +describe('TransactionActivityLog Component', () => { + it('should render properly', () => { + const transaction = { + history: [], + id: 1, + status: 'confirmed', + txParams: { + from: '0x1', + gas: '0x5208', + gasPrice: '0x3b9aca00', + nonce: '0xa4', + to: '0x2', + value: '0x2386f26fc10000', + }, + } + + const wrapper = shallow( + , + { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } } + ) + + assert.ok(wrapper.hasClass('transaction-activity-log')) + assert.ok(wrapper.hasClass('test-class')) + assert.equal(wrapper.find(Card).length, 1) + }) +}) diff --git a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js new file mode 100644 index 000000000..85d56a6a2 --- /dev/null +++ b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js @@ -0,0 +1,27 @@ +import assert from 'assert' +import proxyquire from 'proxyquire' + +let mapStateToProps + +proxyquire('../transaction-activity-log.container.js', { + 'react-redux': { + connect: ms => { + mapStateToProps = ms + return () => ({}) + }, + }, +}) + +describe('TransactionActivityLog container', () => { + describe('mapStateToProps()', () => { + it('should return the correct props', () => { + const mockState = { + metamask: { + conversionRate: 280.45, + }, + } + + assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45 }) + }) + }) +}) diff --git a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.util.test.js b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.util.test.js new file mode 100644 index 000000000..586500408 --- /dev/null +++ b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.util.test.js @@ -0,0 +1,208 @@ +import assert from 'assert' +import { getActivities } from '../transaction-activity-log.util' + +describe('getActivities', () => { + it('should return no activities for an empty history', () => { + const transaction = { + history: [], + id: 1, + status: 'confirmed', + txParams: { + from: '0x1', + gas: '0x5208', + gasPrice: '0x3b9aca00', + nonce: '0xa4', + to: '0x2', + value: '0x2386f26fc10000', + }, + } + + assert.deepEqual(getActivities(transaction), []) + }) + + it('should return activities for a transaction\'s history', () => { + const transaction = { + history: [ + { + id: 5559712943815343, + loadingDefaults: true, + metamaskNetworkId: '3', + status: 'unapproved', + time: 1535507561452, + txParams: { + from: '0x1', + gas: '0x5208', + gasPrice: '0x3b9aca00', + nonce: '0xa4', + to: '0x2', + value: '0x2386f26fc10000', + }, + }, + [ + { + op: 'replace', + path: '/loadingDefaults', + timestamp: 1535507561515, + value: false, + }, + { + op: 'add', + path: '/gasPriceSpecified', + value: true, + }, + { + op: 'add', + path: '/gasLimitSpecified', + value: true, + }, + { + op: 'add', + path: '/estimatedGas', + value: '0x5208', + }, + ], + [ + { + note: '#newUnapprovedTransaction - adding the origin', + op: 'add', + path: '/origin', + timestamp: 1535507561516, + value: 'MetaMask', + }, + [], + ], + [ + { + note: 'confTx: user approved transaction', + op: 'replace', + path: '/txParams/gasPrice', + timestamp: 1535664571504, + value: '0x77359400', + }, + ], + [ + { + note: 'txStateManager: setting status to approved', + op: 'replace', + path: '/status', + timestamp: 1535507564302, + value: 'approved', + }, + ], + [ + { + note: 'transactions#approveTransaction', + op: 'add', + path: '/txParams/nonce', + timestamp: 1535507564439, + value: '0xa4', + }, + { + op: 'add', + path: '/nonceDetails', + value: { + local: {}, + network: {}, + params: {}, + }, + }, + ], + [ + { + note: 'transactions#publishTransaction', + op: 'replace', + path: '/status', + timestamp: 1535507564518, + value: 'signed', + }, + { + op: 'add', + path: '/rawTx', + value: '0xf86b81a4843b9aca008252089450a9d56c2b8ba9a5c7f2c08c3d26e0499f23a706872386f26fc10000802aa007b30119fc4fc5954fad727895b7e3ba80a78d197e95703cc603bcf017879151a01c50beda40ffaee541da9c05b9616247074f25f392800e0ad6c7a835d5366edf', + }, + ], + [], + [ + { + note: 'transactions#setTxHash', + op: 'add', + path: '/hash', + timestamp: 1535507564658, + value: '0x7acc4987b5c0dfa8d423798a8c561138259de1f98a62e3d52e7e83c0e0dd9fb7', + }, + ], + [ + { + note: 'txStateManager - add submitted time stamp', + op: 'add', + path: '/submittedTime', + timestamp: 1535507564660, + value: 1535507564660, + }, + ], + [ + { + note: 'txStateManager: setting status to submitted', + op: 'replace', + path: '/status', + timestamp: 1535507564665, + value: 'submitted', + }, + ], + [ + { + note: 'transactions/pending-tx-tracker#event: tx:block-update', + op: 'add', + path: '/firstRetryBlockNumber', + timestamp: 1535507575476, + value: '0x3bf624', + }, + ], + [ + { + note: 'txStateManager: setting status to confirmed', + op: 'replace', + path: '/status', + timestamp: 1535507615993, + value: 'confirmed', + }, + ], + ], + id: 1, + status: 'confirmed', + txParams: { + from: '0x1', + gas: '0x5208', + gasPrice: '0x3b9aca00', + nonce: '0xa4', + to: '0x2', + value: '0x2386f26fc10000', + }, + } + + const expectedResult = [ + { + 'eventKey': 'transactionCreated', + 'timestamp': 1535507561452, + 'value': '0x2386f26fc10000', + }, + { + 'eventKey': 'transactionUpdatedGas', + 'timestamp': 1535664571504, + 'value': '0x77359400', + }, + { + 'eventKey': 'transactionSubmitted', + 'timestamp': 1535507564665, + 'value': undefined, + }, + { + 'eventKey': 'transactionConfirmed', + 'timestamp': 1535507615993, + 'value': undefined, + }, + ] + + assert.deepEqual(getActivities(transaction), expectedResult) + }) +}) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js index 4cba8cf15..c0cf099d0 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js @@ -1,7 +1,10 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' +import classnames from 'classnames' import { getActivities } from './transaction-activity-log.util' import Card from '../card' +import { getEthConversionFromWeiHex } from '../../helpers/conversions.util' +import { ETH } from '../../constants/common' export default class TransactionActivityLog extends PureComponent { static contextTypes = { @@ -10,6 +13,8 @@ export default class TransactionActivityLog extends PureComponent { static propTypes = { transaction: PropTypes.object, + className: PropTypes.string, + conversionRate: PropTypes.number, } state = { @@ -35,54 +40,36 @@ export default class TransactionActivityLog extends PureComponent { } renderActivity (activity, index) { + const { conversionRate } = this.props + const { eventKey, value } = activity + const ethValue = getEthConversionFromWeiHex({ value, toCurrency: ETH, conversionRate }) + return (
- { this.renderActivityText(activity) } -
- ) - } - - renderActivityText (activity) { - const { eventKey, value, valueDescriptionKey } = activity - - return ( -
- { `Transaction ` } - { `${eventKey}` } - { - valueDescriptionKey && value - ? ( - - { ` with a ${valueDescriptionKey} of ` } - { value } - . - - ) : '.' - } +
+ { this.context.t(eventKey, [ethValue]) } +
) } render () { const { t } = this.context + const { className } = this.props const { activities } = this.state return ( -
+
- { - activities.map((activity, index) => ( - this.renderActivity(activity, index) - )) - } + { activities.map((activity, index) => this.renderActivity(activity, index)) }
diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.container.js b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js new file mode 100644 index 000000000..4c8b6d971 --- /dev/null +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux' +import TransactionActivityLog from './transaction-activity-log.component' +import { conversionRateSelector } from '../../selectors' + +const mapStateToProps = state => { + return { + conversionRate: conversionRateSelector(state), + } +} + +export default connect(mapStateToProps)(TransactionActivityLog) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js index fe780788a..fff0b68dc 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js @@ -3,31 +3,37 @@ const STATUS_PATH = '/status' const GAS_PRICE_PATH = '/txParams/gasPrice' // status constants -const STATUS_UNAPPROVED = 'unapproved' -const STATUS_SUBMITTED = 'submitted' -const STATUS_CONFIRMED = 'confirmed' -const STATUS_DROPPED = 'dropped' +const UNAPPROVED_STATUS = 'unapproved' +const SUBMITTED_STATUS = 'submitted' +const CONFIRMED_STATUS = 'confirmed' +const DROPPED_STATUS = 'dropped' // op constants const REPLACE_OP = 'replace' +// event constants +const TRANSACTION_CREATED_EVENT = 'transactionCreated' +const TRANSACTION_UPDATED_GAS_EVENT = 'transactionUpdatedGas' +const TRANSACTION_SUBMITTED_EVENT = 'transactionSubmitted' +const TRANSACTION_CONFIRMED_EVENT = 'transactionConfirmed' +const TRANSACTION_DROPPED_EVENT = 'transactionDropped' + const eventPathsHash = { [STATUS_PATH]: true, [GAS_PRICE_PATH]: true, } const statusHash = { - [STATUS_SUBMITTED]: true, - [STATUS_CONFIRMED]: true, - [STATUS_DROPPED]: true, + [SUBMITTED_STATUS]: TRANSACTION_SUBMITTED_EVENT, + [CONFIRMED_STATUS]: TRANSACTION_CONFIRMED_EVENT, + [DROPPED_STATUS]: TRANSACTION_DROPPED_EVENT, } -function eventCreator (eventKey, timestamp, value, valueDescriptionKey) { +function eventCreator (eventKey, timestamp, value) { return { eventKey, timestamp, value, - valueDescriptionKey, } } @@ -36,9 +42,9 @@ export function getActivities (transaction) { return history.reduce((acc, base) => { // First history item should be transaction creation - if (!Array.isArray(base) && base.status === STATUS_UNAPPROVED && base.txParams) { + if (!Array.isArray(base) && base.status === UNAPPROVED_STATUS && base.txParams) { const { time, txParams: { value } = {} } = base - return acc.concat(eventCreator('created', time, value, 'value')) + return acc.concat(eventCreator(TRANSACTION_CREATED_EVENT, time, value)) } else if (Array.isArray(base)) { const events = [] @@ -49,14 +55,14 @@ export function getActivities (transaction) { switch (path) { case STATUS_PATH: { if (value in statusHash) { - events.push(eventCreator(value, timestamp)) + events.push(eventCreator(statusHash[value], timestamp)) } break } case GAS_PRICE_PATH: { - events.push(eventCreator('updated', timestamp, value, 'gasPrice')) + events.push(eventCreator(TRANSACTION_UPDATED_GAS_EVENT, timestamp, value)) break } diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index 38f5f1c50..f271b5683 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -35,6 +35,7 @@ BigNumber.config({ // Big Number Constants const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000') const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000') +const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber('1') // Individual Setters const convert = R.invoker(1, 'times') @@ -52,10 +53,12 @@ const toBigNumber = { const toNormalizedDenomination = { WEI: bigNumber => bigNumber.div(BIG_NUMBER_WEI_MULTIPLIER), GWEI: bigNumber => bigNumber.div(BIG_NUMBER_GWEI_MULTIPLIER), + ETH: bigNumber => bigNumber.div(BIG_NUMBER_ETH_MULTIPLIER), } const toSpecifiedDenomination = { WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(), GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(9), + ETH: bigNumber => bigNumber.times(BIG_NUMBER_ETH_MULTIPLIER).round(9), } const baseChange = { hex: n => n.toString(16),