diff --git a/app/src/modules/storage.js b/app/src/modules/storage.js index 605dcfc130..5429801f94 100644 --- a/app/src/modules/storage.js +++ b/app/src/modules/storage.js @@ -11,6 +11,7 @@ export const setConfig = ({ }; export const readConfig = () => { - const value = storage.get('config'); + // Ensure to return empty config if storage does not hold any value + const value = storage.get('config') || {}; win.send({ event: 'configRetrieved', value }); }; diff --git a/app/src/modules/storage.test.js b/app/src/modules/storage.test.js new file mode 100644 index 0000000000..02d569a068 --- /dev/null +++ b/app/src/modules/storage.test.js @@ -0,0 +1,25 @@ +import { expect } from 'chai'; +import { spy } from 'sinon'; +import win from './win'; +import { readConfig } from './storage'; + +jest.mock('electron-store'); + +describe('Storage', () => { + const winSendSpy = spy(win, 'send'); + + afterEach(() => { + win.eventStack.length = 0; + winSendSpy.restore(); + }); + + describe('readConfig', () => { + it('should return empty config if storage has no config set', () => { + // Act + readConfig(); + + // Assert + expect(winSendSpy).to.have.been.calledWith({ event: 'configRetrieved', value: {} }); + }); + }); +}); diff --git a/src/components/screens/multiSignature/result/result.js b/src/components/screens/multiSignature/result/result.js index c531c095e6..ca963d3242 100644 --- a/src/components/screens/multiSignature/result/result.js +++ b/src/components/screens/multiSignature/result/result.js @@ -14,7 +14,8 @@ const Result = ({ const [copied, setCopied] = useState(false); const onDownload = () => { - downloadJSON(transaction, `tx-${transactions.signedTransaction.id}`); + const transactionId = transaction.id.toString('hex'); + downloadJSON(transaction, `tx-${transactionId}`); }; const onCopy = () => { diff --git a/src/components/shared/transactionResult/index.js b/src/components/shared/transactionResult/index.js index 196948d48e..2646378c8d 100644 --- a/src/components/shared/transactionResult/index.js +++ b/src/components/shared/transactionResult/index.js @@ -1,9 +1,11 @@ /* eslint-disable complexity */ import React from 'react'; +import { useSelector } from 'react-redux'; import { getErrorReportMailto, isEmpty } from '@utils/helpers'; import { transactionToJSON } from '@utils/transaction'; import { TertiaryButton } from '@toolbox/buttons'; import Illustration from '@toolbox/illustration'; +import { selectActiveTokenNetwork } from '@store/selectors'; import styles from './transactionResult.css'; const illustrations = { @@ -52,36 +54,48 @@ export const getBroadcastStatus = (transactions, isHardwareWalletError) => { export const TransactionResult = ({ title, message, t, status, children, illustration, className, -}) => ( -
- { - typeof illustration === 'string' - ? - : React.cloneElement(illustration) - } -

{title}

-

{message}

- {children} - { - errorTypes.includes(status.code) - ? ( - <> -

{t('Does the problem still persist?')}

- - - {t('Report the error via email')} - - - - ) - : null - } -
-); +}) => { + const network = useSelector(selectActiveTokenNetwork); + + return ( +
+ { + typeof illustration === 'string' + ? + : React.cloneElement(illustration) + } +

{title}

+

{message}

+ {children} + { + errorTypes.includes(status.code) + ? ( + <> +

{t('Does the problem still persist?')}

+ + + {t('Report the error via email')} + + + + ) + : null + } +
+ ); +}; export default TransactionResult; diff --git a/src/store/actions/transactions.js b/src/store/actions/transactions.js index c2a5d765db..bb319fcbcb 100644 --- a/src/store/actions/transactions.js +++ b/src/store/actions/transactions.js @@ -89,7 +89,7 @@ export const resetTransactionResult = () => ({ * Calls transactionAPI.create for create the tx object that will broadcast * @param {Object} data * @param {String} data.recipientAddress - * @param {Number} data.amount - In raw format (satoshis, beddows) + * @param {Number} data.amount - In raw format (satoshi, beddows) * @param {Number} data.fee - In raw format, used for updating the TX List. * @param {Number} data.dynamicFeePerByte - In raw format, used for creating BTC transaction. * @param {Number} data.reference - Data field for LSK transactions @@ -133,7 +133,7 @@ export const transactionCreated = data => async (dispatch, getState) => { * Calls transactionAPI.broadcast function for put the tx object (signed) into the network * @param {Object} transaction * @param {String} transaction.recipientAddress - * @param {Number} transaction.amount - In raw format (satoshis, beddows) + * @param {Number} transaction.amount - In raw format (satoshi, beddows) * @param {Number} transaction.fee - In raw format, used for updating the TX List. * @param {Number} transaction.dynamicFeePerByte - In raw format, used for creating BTC transaction. * @param {Number} transaction.reference - Data field for LSK transactions diff --git a/src/store/selectors.js b/src/store/selectors.js index dc06bc9fd2..d7bdc40939 100644 --- a/src/store/selectors.js +++ b/src/store/selectors.js @@ -14,6 +14,7 @@ const selectBookmark = (state, address) => const selectSettings = state => state.settings; const selectServiceUrl = state => state.network.networks?.LSK?.serviceUrl; const selectCurrentBlockHeight = state => state.blocks.latestBlocks[0]?.height || 0; +const selectActiveTokenNetwork = state => state.network.networks[state.settings.token.active]; export { selectBookmark, @@ -30,4 +31,5 @@ export { selectAccountBalance, selectServiceUrl, selectCurrentBlockHeight, + selectActiveTokenNetwork, }; diff --git a/src/utils/api/http.js b/src/utils/api/http.js index c4223f4321..47fb8cf2ef 100644 --- a/src/utils/api/http.js +++ b/src/utils/api/http.js @@ -29,10 +29,12 @@ const http = ({ }, ...restOptions, }) - .then((response) => { + .then(async (response) => { if (!response.ok) { + const { message } = await response.json(); const error = Error(response.statusText); error.code = response.status; + error.message = message; throw error; } return response.json(); diff --git a/src/utils/api/http.test.js b/src/utils/api/http.test.js index 4dbbbcc61a..7beeadc88f 100644 --- a/src/utils/api/http.test.js +++ b/src/utils/api/http.test.js @@ -54,12 +54,14 @@ describe('HTTP', () => { it('should throw error when response.ok is false', async () => { const statusText = 'Response.ok is false'; + const message = 'api error'; global.fetch = jest.fn(() => Promise.resolve({ ok: false, - status: 200, + status: 400, statusText, + json: () => Promise.resolve({ message }), })); - await expect(http(data)).rejects.toEqual(Error(statusText)); + await expect(http(data)).rejects.toEqual(Error(message)); }); it('should throw error', async () => { diff --git a/src/utils/api/network/lsk.js b/src/utils/api/network/lsk.js index 307f7283b2..c4751895a5 100644 --- a/src/utils/api/network/lsk.js +++ b/src/utils/api/network/lsk.js @@ -25,7 +25,7 @@ export const getNetworkStatus = ({ network, }); -const getServiceUrl = ({ name = networkKeys.mainNet, address = 'http://localhost:4000' }) => { +const getServiceUrl = ({ name, address = 'http://localhost:4000' }) => { if ([networkKeys.mainNet, networkKeys.testNet].includes(name)) { return networks[name].serviceUrl; } diff --git a/src/utils/getNetwork.js b/src/utils/getNetwork.js index 6a279d912f..46a433a4de 100644 --- a/src/utils/getNetwork.js +++ b/src/utils/getNetwork.js @@ -8,7 +8,8 @@ export const getNetworksList = () => name, })); -export const getNetworkName = network => network.name || 'customNode'; +// Return mainnet as back off network name when cache/local storage does not exists +export const getNetworkName = network => network.name || networkKeys.mainNet; /** * Returns human readable error messages diff --git a/src/utils/getNetwork.test.js b/src/utils/getNetwork.test.js index d461d2cbec..725351b49f 100644 --- a/src/utils/getNetwork.test.js +++ b/src/utils/getNetwork.test.js @@ -16,31 +16,31 @@ describe('Utils: getNetwork', () => { }); }); - describe.skip('getNetworkName', () => { - it('should discover mainnet', () => { + describe('getNetworkName', () => { + it('should return mainnet if network config does not have name set', () => { + const network = {}; + expect(getNetworkName(network)).toEqual('mainnet'); + }); + + it('should return customNode', () => { const network = { name: 'customNode', }; - expect(getNetworkName(network, 'LSK')).toEqual('mainnet'); + expect(getNetworkName(network)).toEqual(network.name); }); - it('should discover testnet', () => { + it('should return testnet', () => { const network = { - name: 'customNode', + name: 'testnet', }; - expect(getNetworkName(network, 'LSK')).toEqual('testnet'); + expect(getNetworkName(network)).toEqual(network.name); }); - it('should mark as customNode otherwise', () => { + it('should return mainnet', () => { const network = { - name: 'customNode', - networks: { - LSK: { - nethash: 'sample_hash', - }, - }, + name: 'mainnet', }; - expect(getNetworkName(network, 'LSK')).toEqual('customNode'); + expect(getNetworkName(network)).toEqual(network.name); }); }); diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 610e7e1071..fca77afc87 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -57,10 +57,20 @@ export const filterObjectPropsWithValue = (object = {}, value) => ( * @param {string} error - error message to put into the email body * @returns {sting} mailto link with recipient, subject, and body */ -export const getErrorReportMailto = (error = 'Unknown error occured') => { +export const getErrorReportMailto = ({ + error = 'Unknown error occurred', errorMessage, networkIdentifier, serviceUrl, liskCoreVersion, +}) => { const recipient = 'hubdev@lisk.io'; const subject = `User Reported Error - Lisk - ${VERSION}`; // eslint-disable-line no-undef - const body = encodeURIComponent(`\nImportant metadata for the team, please do not edit: \n\n${error}\n`); + const body = encodeURIComponent(` + \nImportant metadata for the team, please do not edit: + \r + Lisk Core Version: ${liskCoreVersion}, NetworkIdentifier: ${networkIdentifier}, ServiceURL: ${serviceUrl} + \r + Error Message: ${errorMessage} + \r + Transaction: ${error} + `); return `mailto:${recipient}?&subject=${subject}&body=${body}`; };