diff --git a/web/cashtab/src/components/Send/SendToken.js b/web/cashtab/src/components/Send/SendToken.js --- a/web/cashtab/src/components/Send/SendToken.js +++ b/web/cashtab/src/components/Send/SendToken.js @@ -24,13 +24,21 @@ isValidTokenPrefix, } from '@components/Common/Ticker.js'; import { Event } from '@utils/GoogleAnalytics'; -import { formatBalance } from '@utils/cashMethods'; +import { formatBalance, isValidStoredWallet } from '@utils/cashMethods'; -const SendToken = ({ tokenId }) => { +const SendToken = ({ tokenId, jestBCH }) => { const { wallet, tokens, slpBalancesAndUtxos, apiError } = React.useContext( WalletContext, ); - const token = tokens.find(token => token.tokenId === tokenId); + // If this wallet has migrated to latest storage structure, get token info from there + // If not, use the tokens object (unless it's undefined, in which case use an empty array) + const liveTokenState = + isValidStoredWallet(wallet) && wallet.state.tokens + ? wallet.state.tokens + : tokens + ? tokens + : []; + const token = liveTokenState.find(token => token.tokenId === tokenId); const [queryStringText, setQueryStringText] = useState(null); const [sendTokenAddressError, setSendTokenAddressError] = useState(false); const [sendTokenAmountError, setSendTokenAmountError] = useState(false); @@ -48,7 +56,8 @@ const [loading, setLoading] = useState(false); const { getBCH, getRestUrl, sendToken } = useBCH(); - const BCH = getBCH(); + // jestBCH is only ever specified for unit tests, otherwise app will use getBCH(); + const BCH = jestBCH ? jestBCH : getBCH(); // Keep this function around for re-enabling later // eslint-disable-next-line no-unused-vars @@ -147,7 +156,10 @@ } } setSendTokenAmountError(error); - setFormData(p => ({ ...p, [name]: value })); + setFormData(p => ({ + ...p, + [name]: value, + })); }; const handleTokenAddressChange = e => { @@ -236,11 +248,17 @@ -
+ -
+
{apiError || sendTokenAmountError || sendTokenAddressError ? ( @@ -352,7 +374,11 @@ /> )} {apiError && ( -

+

An error occured on our end. Reconnecting... diff --git a/web/cashtab/src/components/Send/__tests__/SendToken.test.js b/web/cashtab/src/components/Send/__tests__/SendToken.test.js new file mode 100644 --- /dev/null +++ b/web/cashtab/src/components/Send/__tests__/SendToken.test.js @@ -0,0 +1,120 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { ThemeProvider } from 'styled-components'; +import { theme } from '@assets/styles/theme'; +import SendToken from '@components/Send/SendToken'; +import BCHJS from '@psf/bch-js'; +import { + walletWithBalancesAndTokens, + walletWithBalancesAndTokensWithCorrectState, + walletWithBalancesAndTokensWithEmptyState, +} from '../../Wallet/__mocks__/walletAndBalancesMock'; +import { BrowserRouter as Router } from 'react-router-dom'; + +let realUseContext; +let useContextMock; + +beforeEach(() => { + realUseContext = React.useContext; + useContextMock = React.useContext = jest.fn(); + + // Mock method not implemented in JSDOM + // See reference at https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }); +}); + +afterEach(() => { + React.useContext = realUseContext; +}); + +test('Wallet with BCH balances and tokens', () => { + const testBCH = new BCHJS(); + useContextMock.mockReturnValue(walletWithBalancesAndTokens); + const component = renderer.create( + + + + + , + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Wallet with BCH balances and tokens and state field', () => { + const testBCH = new BCHJS(); + useContextMock.mockReturnValue(walletWithBalancesAndTokensWithCorrectState); + const component = renderer.create( + + + + + , + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Wallet with BCH balances and tokens and state field, but no params in state', () => { + const testBCH = new BCHJS(); + useContextMock.mockReturnValue(walletWithBalancesAndTokensWithEmptyState); + const component = renderer.create( + + + + + , + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('Without wallet defined', () => { + const testBCH = new BCHJS(); + useContextMock.mockReturnValue({ + wallet: {}, + balances: { totalBalance: 0 }, + loading: false, + }); + const component = renderer.create( + + + + + , + ); + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/web/cashtab/src/components/Send/__tests__/__snapshots__/SendToken.test.js.snap b/web/cashtab/src/components/Send/__tests__/__snapshots__/SendToken.test.js.snap new file mode 100644 --- /dev/null +++ b/web/cashtab/src/components/Send/__tests__/__snapshots__/SendToken.test.js.snap @@ -0,0 +1,789 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP @generated + +exports[`Wallet with BCH balances and tokens 1`] = ` +Array [ +

+

+ Available balance +

+

+ 6.001 + + TBS +

+
, +
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + identicon of tokenId bd1acc4c986de57af8d6d2a64aecad8c30ee80f37ae9d066d758923732ddc9ba + + + + TBS + + + + + max + + + + +
+
+
+
+ +
+
+
+
+
+
+ +
+ +
+
+
+
, +] +`; + +exports[`Wallet with BCH balances and tokens and state field 1`] = ` +Array [ +
+

+ Available balance +

+

+ 6.001 + + TBS +

+
, +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + identicon of tokenId bd1acc4c986de57af8d6d2a64aecad8c30ee80f37ae9d066d758923732ddc9ba + + + + TBS + + + + + max + + + + +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
, +] +`; + +exports[`Wallet with BCH balances and tokens and state field, but no params in state 1`] = ` +Array [ +
+

+ Available balance +

+

+ 6.001 + + TBS +

+
, +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + identicon of tokenId bd1acc4c986de57af8d6d2a64aecad8c30ee80f37ae9d066d758923732ddc9ba + + + + TBS + + + + + max + + + + +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
, +] +`; + +exports[`Without wallet defined 1`] = `null`;