Changeset View
Changeset View
Standalone View
Standalone View
cashtab/src/components/Configure/__tests__/Configure.test.tsx
- This file was moved from cashtab/src/components/Configure/__tests__/Configure.test.js.
| // Copyright (c) 2024 The Bitcoin developers | // Copyright (c) 2024 The Bitcoin developers | ||||
| // Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
| import React from 'react'; | import React from 'react'; | ||||
| import { | import { | ||||
| walletWithXecAndTokens, | walletWithXecAndTokens, | ||||
| vipTokenChronikTokenMocks, | vipTokenChronikTokenMocks, | ||||
| cachetTokenAndTx, | cachetTokenAndTx, | ||||
| bearTokenAndTx, | |||||
| requiredUtxoThisToken, | requiredUtxoThisToken, | ||||
| } from 'components/App/fixtures/mocks'; | } from 'components/App/fixtures/mocks'; | ||||
| import { render, screen, waitFor } from '@testing-library/react'; | import { render, screen, waitFor } from '@testing-library/react'; | ||||
| import userEvent from '@testing-library/user-event'; | import userEvent from '@testing-library/user-event'; | ||||
| import '@testing-library/jest-dom'; | import '@testing-library/jest-dom'; | ||||
| import 'fake-indexeddb/auto'; | import 'fake-indexeddb/auto'; | ||||
| import localforage from 'localforage'; | import localforage from 'localforage'; | ||||
| import { when } from 'jest-when'; | import { when } from 'jest-when'; | ||||
| import appConfig from 'config/app'; | import appConfig from 'config/app'; | ||||
| import { prepareContext, mockPrice } from 'test'; | |||||
| import { ThemeProvider } from 'styled-components'; | |||||
| import { theme } from 'assets/styles/theme'; | |||||
| import { MemoryRouter } from 'react-router-dom'; | |||||
| import { WalletProvider } from 'wallet/context'; | |||||
| import { ChronikClient } from 'chronik-client'; | |||||
| import { Ecc } from 'ecash-lib'; | |||||
| import { Agora } from 'ecash-agora'; | |||||
| import { | import { | ||||
| initializeCashtabStateForTests, | MockAgora, | ||||
| clearLocalForage, | MockChronikClient, | ||||
| } from 'components/App/fixtures/helpers'; | } from '../../../../../modules/mock-chronik-client'; | ||||
| import CashtabTestWrapper from 'components/App/fixtures/CashtabTestWrapper'; | import App from 'components/App/App'; | ||||
| import { explorer } from 'config/explorer'; | import { explorer } from 'config/explorer'; | ||||
| import { undecimalizeTokenAmount } from 'wallet'; | import { undecimalizeTokenAmount, CashtabWallet } from 'wallet'; | ||||
| import { Ecc } from 'ecash-lib'; | |||||
| import { MockAgora } from '../../../../../modules/mock-chronik-client/dist'; | interface ConfigureTestWrapperProps { | ||||
| chronik: MockChronikClient; | |||||
| agora: MockAgora; | |||||
| ecc: Ecc; | |||||
| theme: any; | |||||
| route?: string; | |||||
| } | |||||
| const ConfigureTestWrapper: React.FC<ConfigureTestWrapperProps> = ({ | |||||
| chronik, | |||||
| agora, | |||||
| ecc, | |||||
| theme, | |||||
| route = '/configure', | |||||
| }) => ( | |||||
| <WalletProvider | |||||
| chronik={chronik as unknown as ChronikClient} | |||||
| agora={agora as unknown as Agora} | |||||
| ecc={ecc} | |||||
| > | |||||
| <MemoryRouter initialEntries={[route]}> | |||||
| <ThemeProvider theme={theme}> | |||||
| <App /> | |||||
| </ThemeProvider> | |||||
| </MemoryRouter> | |||||
| </WalletProvider> | |||||
| ); | |||||
| describe('<Configure />', () => { | describe('<Configure />', () => { | ||||
| const ecc = new Ecc(); | const ecc = new Ecc(); | ||||
| let user, mockAgora; | let user: ReturnType<typeof userEvent.setup>; | ||||
| let mockAgora: MockAgora; | |||||
| beforeEach(() => { | beforeEach(() => { | ||||
| mockAgora = new MockAgora(); | mockAgora = new MockAgora(); | ||||
| // Set up userEvent | // Set up userEvent | ||||
| user = userEvent.setup(); | user = userEvent.setup(); | ||||
| // Mock the fetch call for Cashtab's price API | // Mock the fetch call for Cashtab's price API | ||||
| global.fetch = jest.fn(); | global.fetch = jest.fn(); | ||||
| const fiatCode = 'usd'; // Use usd until you mock getting settings from localforage | mockPrice(0.00003); | ||||
| const cryptoId = appConfig.coingeckoId; | |||||
| // Keep this in the code, because different URLs will have different outputs requiring different parsing | |||||
| const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`; | |||||
| const xecPrice = 0.00003; | |||||
| const priceResponse = { | |||||
| ecash: { | |||||
| usd: xecPrice, | |||||
| last_updated_at: 1706644626, | |||||
| }, | |||||
| }; | |||||
| when(fetch) | |||||
| .calledWith(priceApiUrl) | |||||
| .mockResolvedValue({ | |||||
| json: () => Promise.resolve(priceResponse), | |||||
| }); | |||||
| // Mock another price URL for a user that changes fiat currency | // Mock another price URL for a user that changes fiat currency | ||||
| const altFiat = 'gbp'; | const altFiat = 'gbp'; | ||||
| const cryptoId = appConfig.coingeckoId; | |||||
| const altFiatPriceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${altFiat}&include_last_updated_at=true`; | const altFiatPriceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${altFiat}&include_last_updated_at=true`; | ||||
| const xecPriceAltFiat = 0.00002; | const xecPriceAltFiat = 0.00002; | ||||
| const altFiatPriceResponse = { | const altFiatPriceResponse = { | ||||
| ecash: { | ecash: { | ||||
| [altFiat]: xecPriceAltFiat, | [altFiat]: xecPriceAltFiat, | ||||
| last_updated_at: 1706644626, | last_updated_at: 1706644626, | ||||
| }, | }, | ||||
| }; | }; | ||||
| when(fetch) | when(fetch) | ||||
| .calledWith(altFiatPriceApiUrl) | .calledWith(altFiatPriceApiUrl) | ||||
| .mockResolvedValue({ | .mockResolvedValue({ | ||||
| json: () => Promise.resolve(altFiatPriceResponse), | json: () => Promise.resolve(altFiatPriceResponse), | ||||
| }); | } as Response); | ||||
| // Mock firma price API call | // Mock firma price API call | ||||
| const firmaPriceGbp = 0.5; | const firmaPriceGbp = 0.5; | ||||
| const firmaPriceResponse = { | const firmaPriceResponse = { | ||||
| usd: { | usd: { | ||||
| gbp: firmaPriceGbp, | gbp: firmaPriceGbp, | ||||
| }, | }, | ||||
| }; | }; | ||||
| when(fetch) | when(fetch) | ||||
| .calledWith( | .calledWith( | ||||
| `https://api.coingecko.com/api/v3/simple/price?ids=usd&vs_currencies=${altFiat}`, | `https://api.coingecko.com/api/v3/simple/price?ids=usd&vs_currencies=${altFiat}`, | ||||
| ) | ) | ||||
| .mockResolvedValue({ | .mockResolvedValue({ | ||||
| json: () => Promise.resolve(firmaPriceResponse), | json: () => Promise.resolve(firmaPriceResponse), | ||||
| } as Response); | |||||
| }); | }); | ||||
| }); | |||||
| afterEach(async () => { | afterEach(async () => { | ||||
| jest.clearAllMocks(); | jest.clearAllMocks(); | ||||
| await clearLocalForage(localforage); | await localforage.clear(); | ||||
| }); | }); | ||||
| it('Setting "Send Confirmations" settings will show send confirmations', async () => { | it('Setting "Send Confirmations" settings will show send confirmations', async () => { | ||||
| const mockedChronik = await initializeCashtabStateForTests( | const tokenMocks = new Map(); | ||||
| walletWithXecAndTokens, | // Add BEAR token mock | ||||
| tokenMocks.set(bearTokenAndTx.token.tokenId, { | |||||
| tx: bearTokenAndTx.tx, | |||||
| tokenInfo: bearTokenAndTx.token, | |||||
| }); | |||||
| const mockedChronik = await prepareContext( | |||||
| localforage, | localforage, | ||||
| [walletWithXecAndTokens], | |||||
| tokenMocks, | |||||
| ); | ); | ||||
| const hex = | const hex = | ||||
| '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a210200000064416a14b2f97b4b24a409799b68a6da2d34ada9fa0f305eeffe11d9234cd8ee17dcb033de65840107dd52c35207fb2d2a88eadac270e582a8bc7cd66df4437800234121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acdb6c0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a210200000064416a14b2f97b4b24a409799b68a6da2d34ada9fa0f305eeffe11d9234cd8ee17dcb033de65840107dd52c35207fb2d2a88eadac270e582a8bc7cd66df4437800234121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acdb6c0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | ||||
| const txid = | const txid = | ||||
| '5f334f32bec07b1029ae579460c704e33ba05b91e3bc2bba9ee215bc585cd6ab'; | '5f334f32bec07b1029ae579460c704e33ba05b91e3bc2bba9ee215bc585cd6ab'; | ||||
| mockedChronik.setBroadcastTx(hex, txid); | mockedChronik.setBroadcastTx(hex, txid); | ||||
| render( | render( | ||||
| <CashtabTestWrapper | <ConfigureTestWrapper | ||||
| chronik={mockedChronik} | chronik={mockedChronik} | ||||
| agora={mockAgora} | agora={mockAgora} | ||||
| ecc={ecc} | ecc={ecc} | ||||
| theme={theme} | |||||
| />, | />, | ||||
| ); | ); | ||||
| // Default route is home | // Default route is home | ||||
| await screen.findByTestId('tx-history'); | await waitFor(() => | ||||
| expect( | |||||
| screen.queryByTitle('Cashtab Loading'), | |||||
| ).not.toBeInTheDocument(), | |||||
| ); | |||||
| // Click the hamburger menu | // Click the hamburger menu | ||||
| await user.click(screen.queryByTitle('Show Other Screens')); | await user.click(screen.queryByTitle('Show Other Screens')!); | ||||
| // Navigate to Settings screen | // Navigate to Settings screen | ||||
| await user.click( | await user.click( | ||||
| screen.getByRole('button', { | screen.getByRole('button', { | ||||
| name: /Settings/i, | name: /Settings/i, | ||||
| }), | }), | ||||
| ); | ); | ||||
| Show All 37 Lines | it('Setting "Send Confirmations" settings will show send confirmations', async () => { | ||||
| const txSuccessNotification = await screen.findByText('eCash sent'); | const txSuccessNotification = await screen.findByText('eCash sent'); | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect(txSuccessNotification).toHaveAttribute( | expect(txSuccessNotification).toHaveAttribute( | ||||
| 'href', | 'href', | ||||
| `${explorer.blockExplorerUrl}/tx/${txid}`, | `${explorer.blockExplorerUrl}/tx/${txid}`, | ||||
| ), | ), | ||||
| ); | ); | ||||
| }); | }); | ||||
| it('"ABSOLUTE MINIMUM fees" setting is unavailable if wallet holds 0.01 less than required balance of Cachet', async () => { | it('"ABSOLUTE MINIMUM fees" setting is unavailable if wallet holds 0.01 less than required balance of Cachet', async () => { | ||||
| const CACHET_DECIMALS = 2; | const CACHET_DECIMALS = 2; | ||||
| // Modify walletWithXecAndTokens to have the required token for this feature | // Modify walletWithXecAndTokens to have the required token for this feature | ||||
| const walletWithVipToken = { | const walletWithVipToken = { | ||||
| ...walletWithXecAndTokens, | ...walletWithXecAndTokens, | ||||
| state: { | state: { | ||||
| ...walletWithXecAndTokens.state, | ...walletWithXecAndTokens.state, | ||||
| slpUtxos: [ | slpUtxos: [ | ||||
| ...walletWithXecAndTokens.state.slpUtxos, | ...walletWithXecAndTokens.state.slpUtxos, | ||||
| { | { | ||||
| ...requiredUtxoThisToken, | ...requiredUtxoThisToken, | ||||
| path: 1899, | |||||
| token: { | token: { | ||||
| ...requiredUtxoThisToken.token, | ...requiredUtxoThisToken.token, | ||||
| tokenId: appConfig.vipTokens.cachet.tokenId, | tokenId: appConfig.vipTokens.cachet.tokenId, | ||||
| tokenType: { | |||||
| protocol: 'SLP' as const, | |||||
| type: 'SLP_TOKEN_TYPE_FUNGIBLE' as const, | |||||
| number: 1, | |||||
| }, | |||||
| atoms: BigInt( | atoms: BigInt( | ||||
| undecimalizeTokenAmount( | undecimalizeTokenAmount( | ||||
| '999.99', | '999.99', | ||||
| CACHET_DECIMALS, | CACHET_DECIMALS, | ||||
| ), | ), | ||||
| ), | ), | ||||
| }, | }, | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| }; | } as CashtabWallet; | ||||
| const mockedChronik = await initializeCashtabStateForTests( | const tokenMocks = new Map(); | ||||
| walletWithVipToken, | // Add BEAR token mock | ||||
| tokenMocks.set(bearTokenAndTx.token.tokenId, { | |||||
| tx: bearTokenAndTx.tx, | |||||
| tokenInfo: bearTokenAndTx.token, | |||||
| }); | |||||
| tokenMocks.set(appConfig.vipTokens.cachet.tokenId, { | |||||
| tx: cachetTokenAndTx.tx, | |||||
| tokenInfo: cachetTokenAndTx.token, | |||||
| }); | |||||
| const mockedChronik = await prepareContext( | |||||
| localforage, | localforage, | ||||
| [walletWithVipToken], | |||||
| tokenMocks, | |||||
| ); | ); | ||||
| // Make sure the app can get this token's genesis info by calling a mock | render( | ||||
| mockedChronik.setToken( | <ConfigureTestWrapper | ||||
| appConfig.vipTokens.cachet.tokenId, | chronik={mockedChronik} | ||||
| cachetTokenAndTx.token, | agora={mockAgora} | ||||
| ); | ecc={ecc} | ||||
| mockedChronik.setTx( | theme={theme} | ||||
| appConfig.vipTokens.cachet.tokenId, | />, | ||||
| cachetTokenAndTx.tx, | |||||
| ); | ); | ||||
| render(<CashtabTestWrapper chronik={mockedChronik} ecc={ecc} />); | |||||
| // Default route is home | // Default route is home | ||||
| await screen.findByTestId('tx-history'); | await waitFor(() => | ||||
| expect( | |||||
| screen.queryByTitle('Cashtab Loading'), | |||||
| ).not.toBeInTheDocument(), | |||||
| ); | |||||
| // Click the hamburger menu | // Click the hamburger menu | ||||
| await user.click(screen.queryByTitle('Show Other Screens')); | await user.click(screen.queryByTitle('Show Other Screens')!); | ||||
| await user.click( | await user.click( | ||||
| screen.getByRole('button', { | screen.getByRole('button', { | ||||
| name: /Settings/i, | name: /Settings/i, | ||||
| }), | }), | ||||
| ); | ); | ||||
| // Now we see the Settings screen | // Now we see the Settings screen | ||||
| expect(screen.getByTitle('Settings')).toBeInTheDocument(); | expect(screen.getByTitle('Settings')).toBeInTheDocument(); | ||||
| // We DO NOT see VIP settings | // We DO NOT see VIP settings | ||||
| expect(screen.queryByText('VIP Settings')).not.toBeInTheDocument(); | expect(screen.queryByText('VIP Settings')).not.toBeInTheDocument(); | ||||
| // We DO NOT see the CACHET token icon | // We DO NOT see the CACHET token icon | ||||
| expect( | expect( | ||||
| screen.queryByAltText( | screen.queryByAltText( | ||||
| `icon for ${appConfig.vipTokens.cachet.tokenId}`, | `icon for ${appConfig.vipTokens.cachet.tokenId}`, | ||||
| ), | ), | ||||
| ).not.toBeInTheDocument(); | ).not.toBeInTheDocument(); | ||||
| }); | }); | ||||
| it('"ABSOLUTE MINIMUM fees" setting is available and effective if wallet holds exactly required balance of Cachet', async () => { | it('"ABSOLUTE MINIMUM fees" setting is available and effective if wallet holds exactly required balance of Cachet', async () => { | ||||
| const CACHET_DECIMALS = 2; | const CACHET_DECIMALS = 2; | ||||
| // Modify walletWithXecAndTokens to have the required token for this feature | // Modify walletWithXecAndTokens to have the required token for this feature | ||||
| const walletWithVipToken = { | const walletWithVipToken = { | ||||
| ...walletWithXecAndTokens, | ...walletWithXecAndTokens, | ||||
| state: { | state: { | ||||
| ...walletWithXecAndTokens.state, | ...walletWithXecAndTokens.state, | ||||
| slpUtxos: [ | slpUtxos: [ | ||||
| ...walletWithXecAndTokens.state.slpUtxos, | ...walletWithXecAndTokens.state.slpUtxos, | ||||
| { | { | ||||
| ...requiredUtxoThisToken, | ...requiredUtxoThisToken, | ||||
| path: 1899, | |||||
| token: { | token: { | ||||
| ...requiredUtxoThisToken.token, | ...requiredUtxoThisToken.token, | ||||
| tokenId: appConfig.vipTokens.cachet.tokenId, | tokenId: appConfig.vipTokens.cachet.tokenId, | ||||
| tokenType: { | |||||
| protocol: 'SLP' as const, | |||||
| type: 'SLP_TOKEN_TYPE_FUNGIBLE' as const, | |||||
| number: 1, | |||||
| }, | |||||
| atoms: BigInt( | atoms: BigInt( | ||||
| undecimalizeTokenAmount( | undecimalizeTokenAmount( | ||||
| appConfig.vipTokens.cachet.vipBalance, | appConfig.vipTokens.cachet.vipBalance, | ||||
| CACHET_DECIMALS, | CACHET_DECIMALS, | ||||
| ), | ), | ||||
| ), | ), | ||||
| }, | }, | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| }; | } as CashtabWallet; | ||||
| const mockedChronik = await initializeCashtabStateForTests( | const tokenMocks = new Map(); | ||||
| walletWithVipToken, | // Add BEAR token mock | ||||
| localforage, | tokenMocks.set(bearTokenAndTx.token.tokenId, { | ||||
| ); | tx: bearTokenAndTx.tx, | ||||
| tokenInfo: bearTokenAndTx.token, | |||||
| }); | |||||
| tokenMocks.set(appConfig.vipTokens.cachet.tokenId, { | |||||
| tx: cachetTokenAndTx.tx, | |||||
| tokenInfo: cachetTokenAndTx.token, | |||||
| }); | |||||
| // Make sure the app can get this token's genesis info by calling a mock | const mockedChronik = await prepareContext( | ||||
| mockedChronik.setToken( | localforage, | ||||
| appConfig.vipTokens.cachet.tokenId, | [walletWithVipToken], | ||||
| cachetTokenAndTx.token, | tokenMocks, | ||||
| ); | |||||
| mockedChronik.setTx( | |||||
| appConfig.vipTokens.cachet.tokenId, | |||||
| cachetTokenAndTx.tx, | |||||
| ); | ); | ||||
| // Can verify in Electrum that this tx is sent at 1.0 sat/byte | // Can verify in Electrum that this tx is sent at 1.0 sat/byte | ||||
| const hex = | const hex = | ||||
| '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a21020000006441a8ae2e6e418b09c8a189547c7412a551617d2f26e55ee5af787ef9ad3f583f6086995640fc06039a04e113dc3d18ce3c51b817f59d31dbb8193dcfa4b7a862664121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acb96d0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a21020000006441a8ae2e6e418b09c8a189547c7412a551617d2f26e55ee5af787ef9ad3f583f6086995640fc06039a04e113dc3d18ce3c51b817f59d31dbb8193dcfa4b7a862664121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acb96d0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | ||||
| const txid = | const txid = | ||||
| 'c16de907537369994417459369faad6595842d569b7b4a9544288ac8a4c81dbb'; | 'c16de907537369994417459369faad6595842d569b7b4a9544288ac8a4c81dbb'; | ||||
| mockedChronik.setBroadcastTx(hex, txid); | mockedChronik.setBroadcastTx(hex, txid); | ||||
| render( | render( | ||||
| <CashtabTestWrapper | <ConfigureTestWrapper | ||||
| chronik={mockedChronik} | chronik={mockedChronik} | ||||
| agora={mockAgora} | agora={mockAgora} | ||||
| ecc={ecc} | ecc={ecc} | ||||
| theme={theme} | |||||
| />, | />, | ||||
| ); | ); | ||||
| // Default route is home | // Default route is home | ||||
| await screen.findByTestId('tx-history'); | await waitFor(() => | ||||
| expect( | |||||
| screen.queryByTitle('Cashtab Loading'), | |||||
| ).not.toBeInTheDocument(), | |||||
| ); | |||||
| // Click the hamburger menu | // Click the hamburger menu | ||||
| await user.click(screen.queryByTitle('Show Other Screens')); | await user.click(screen.queryByTitle('Show Other Screens')!); | ||||
| await user.click( | await user.click( | ||||
| screen.getByRole('button', { | screen.getByRole('button', { | ||||
| name: /Settings/i, | name: /Settings/i, | ||||
| }), | }), | ||||
| ); | ); | ||||
| // Now we see the Settings screen | // Now we see the Settings screen | ||||
| Show All 37 Lines | it('"ABSOLUTE MINIMUM fees" setting is available and effective if wallet holds exactly required balance of Cachet', async () => { | ||||
| const txSuccessNotification = await screen.findByText('eCash sent'); | const txSuccessNotification = await screen.findByText('eCash sent'); | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect(txSuccessNotification).toHaveAttribute( | expect(txSuccessNotification).toHaveAttribute( | ||||
| 'href', | 'href', | ||||
| `${explorer.blockExplorerUrl}/tx/${txid}`, | `${explorer.blockExplorerUrl}/tx/${txid}`, | ||||
| ), | ), | ||||
| ); | ); | ||||
| }); | }); | ||||
| it('Setting "ABSOLUTE MINIMUM fees" settings will reduce fees to absolute min', async () => { | it('Setting "ABSOLUTE MINIMUM fees" settings will reduce fees to absolute min', async () => { | ||||
| // Modify walletWithXecAndTokens to have the required token for this feature | // Modify walletWithXecAndTokens to have the required token for this feature | ||||
| const walletWithVipToken = { | const walletWithVipToken = { | ||||
| ...walletWithXecAndTokens, | ...walletWithXecAndTokens, | ||||
| state: { | state: { | ||||
| ...walletWithXecAndTokens.state, | ...walletWithXecAndTokens.state, | ||||
| slpUtxos: [ | slpUtxos: [ | ||||
| ...walletWithXecAndTokens.state.slpUtxos, | ...walletWithXecAndTokens.state.slpUtxos, | ||||
| requiredUtxoThisToken, | { | ||||
| ...requiredUtxoThisToken, | |||||
| path: 1899, | |||||
| }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| }; | } as CashtabWallet; | ||||
| const mockedChronik = await initializeCashtabStateForTests( | const tokenMocks = new Map(); | ||||
| walletWithVipToken, | // Add BEAR token mock | ||||
| localforage, | tokenMocks.set(bearTokenAndTx.token.tokenId, { | ||||
| ); | tx: bearTokenAndTx.tx, | ||||
| tokenInfo: bearTokenAndTx.token, | |||||
| }); | |||||
| tokenMocks.set(appConfig.vipTokens.grumpy.tokenId, { | |||||
| tx: vipTokenChronikTokenMocks.tx, | |||||
| tokenInfo: vipTokenChronikTokenMocks.token, | |||||
| }); | |||||
| // Make sure the app can get this token's genesis info by calling a mock | const mockedChronik = await prepareContext( | ||||
| mockedChronik.setToken( | localforage, | ||||
| appConfig.vipTokens.grumpy.tokenId, | [walletWithVipToken], | ||||
| vipTokenChronikTokenMocks.token, | tokenMocks, | ||||
| ); | |||||
| mockedChronik.setTx( | |||||
| appConfig.vipTokens.grumpy.tokenId, | |||||
| vipTokenChronikTokenMocks.tx, | |||||
| ); | ); | ||||
| // Can verify in Electrum that this tx is sent at 1.0 sat/byte | // Can verify in Electrum that this tx is sent at 1.0 sat/byte | ||||
| const hex = | const hex = | ||||
| '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a21020000006441a8ae2e6e418b09c8a189547c7412a551617d2f26e55ee5af787ef9ad3f583f6086995640fc06039a04e113dc3d18ce3c51b817f59d31dbb8193dcfa4b7a862664121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acb96d0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | '0200000001fe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a21020000006441a8ae2e6e418b09c8a189547c7412a551617d2f26e55ee5af787ef9ad3f583f6086995640fc06039a04e113dc3d18ce3c51b817f59d31dbb8193dcfa4b7a862664121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff027c150000000000001976a9146ffbe7c7d7bd01295eb1e371de9550339bdcf9fd88acb96d0e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | ||||
| const txid = | const txid = | ||||
| 'c16de907537369994417459369faad6595842d569b7b4a9544288ac8a4c81dbb'; | 'c16de907537369994417459369faad6595842d569b7b4a9544288ac8a4c81dbb'; | ||||
| mockedChronik.setBroadcastTx(hex, txid); | mockedChronik.setBroadcastTx(hex, txid); | ||||
| // Can verify in Electrum that this tx is sent at 1.0 sat/byte | // Can verify in Electrum that this tx is sent at 1.0 sat/byte | ||||
| const tokenSendHex = | const tokenSendHex = | ||||
| '02000000023abaa0b3d97fdc6fb07a535c552fcb379e7bffa6e7e52707b8cf1507bf243e420100000064412f77fbc2321dfa4ada5c554a4528c80862c3184acfcdced5b13d182a890b7199c0ab6b669586cfa7d590e92ada88fe9163557146b7f6c3705163c932154863d64121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dfffffffffe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a210200000064414075e2e616fb9048241e9650f53e5c0fe54c5bc5a88a1e9218321b5e90b4e54fe6f4ddd7f6c345f3bd614c8939ccaba7884112ef8aa6fbc62637d9095d83f4c54121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff040000000000000000406a04534c500001010453454e4420fb4233e8a568993976ed38a81c2671587c5ad09552dedefa78760deed6ff87aa08000000024e160364080000000005f5e09c22020000000000001976a9144e532257c01b310b3b5c1fd947c79a72addf852388ac22020000000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac1b800e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | '02000000023abaa0b3d97fdc6fb07a535c552fcb379e7bffa6e7e52707b8cf1507bf243e420100000064412f77fbc2321dfa4ada5c554a4528c80862c3184acfcdced5b13d182a890b7199c0ab6b669586cfa7d590e92ada88fe9163557146b7f6c3705163c932154863d64121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dfffffffffe667fba52a1aa603a892126e492717eed3dad43bfea7365a7fdd08e051e8a210200000064414075e2e616fb9048241e9650f53e5c0fe54c5bc5a88a1e9218321b5e90b4e54fe6f4ddd7f6c345f3bd614c8939ccaba7884112ef8aa6fbc62637d9095d83f4c54121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff040000000000000000406a04534c500001010453454e4420fb4233e8a568993976ed38a81c2671587c5ad09552dedefa78760deed6ff87aa08000000024e160364080000000005f5e09c22020000000000001976a9144e532257c01b310b3b5c1fd947c79a72addf852388ac22020000000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac1b800e00000000001976a9143a5fb236934ec078b4507c303d3afd82067f8fc188ac00000000'; | ||||
| const tokenSendTxid = | const tokenSendTxid = | ||||
| 'a9981db09af60875966df3f47a80588d0975fec799c658b702b22633604904d1'; | 'a9981db09af60875966df3f47a80588d0975fec799c658b702b22633604904d1'; | ||||
| mockedChronik.setBroadcastTx(tokenSendHex, tokenSendTxid); | mockedChronik.setBroadcastTx(tokenSendHex, tokenSendTxid); | ||||
| render( | render( | ||||
| <CashtabTestWrapper | <ConfigureTestWrapper | ||||
| chronik={mockedChronik} | chronik={mockedChronik} | ||||
| agora={mockAgora} | agora={mockAgora} | ||||
| ecc={ecc} | ecc={ecc} | ||||
| theme={theme} | |||||
| />, | />, | ||||
| ); | ); | ||||
| // Default route is home | // Default route is home | ||||
| await screen.findByTestId('tx-history'); | await waitFor(() => | ||||
| expect( | |||||
| screen.queryByTitle('Cashtab Loading'), | |||||
| ).not.toBeInTheDocument(), | |||||
| ); | |||||
| // Click the hamburger menu | // Click the hamburger menu | ||||
| await user.click(screen.queryByTitle('Show Other Screens')); | await user.click(screen.queryByTitle('Show Other Screens')!); | ||||
| await user.click( | await user.click( | ||||
| screen.getByRole('button', { | screen.getByRole('button', { | ||||
| name: /Settings/i, | name: /Settings/i, | ||||
| }), | }), | ||||
| ); | ); | ||||
| // Now we see the Settings screen | // Now we see the Settings screen | ||||
| ▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | it('Setting "ABSOLUTE MINIMUM fees" settings will reduce fees to absolute min', async () => { | ||||
| // Actually we can't update the utxo set now, so we add a separate test to confirm | // Actually we can't update the utxo set now, so we add a separate test to confirm | ||||
| // the feature is disabled even if it was set to true but then token balance decreased | // the feature is disabled even if it was set to true but then token balance decreased | ||||
| // TODO we can test wallet utxo set updates if we connect some Cashtab integration tests | // TODO we can test wallet utxo set updates if we connect some Cashtab integration tests | ||||
| // to regtest | // to regtest | ||||
| // See SendXec test, "If the user has minFeeSends set to true but no longer has the right token amount, the feature is disabled" | // See SendXec test, "If the user has minFeeSends set to true but no longer has the right token amount, the feature is disabled" | ||||
| }); | }); | ||||
| it('We can choose a new fiat currency', async () => { | it('We can choose a new fiat currency', async () => { | ||||
| const mockedChronik = await initializeCashtabStateForTests( | const tokenMocks = new Map(); | ||||
| walletWithXecAndTokens, | // Add BEAR token mock | ||||
| tokenMocks.set(bearTokenAndTx.token.tokenId, { | |||||
| tx: bearTokenAndTx.tx, | |||||
| tokenInfo: bearTokenAndTx.token, | |||||
| }); | |||||
| const mockedChronik = await prepareContext( | |||||
| localforage, | localforage, | ||||
| [walletWithXecAndTokens], | |||||
| tokenMocks, | |||||
| ); | ); | ||||
| render( | render( | ||||
| <CashtabTestWrapper | <ConfigureTestWrapper | ||||
| chronik={mockedChronik} | chronik={mockedChronik} | ||||
| agora={mockAgora} | agora={mockAgora} | ||||
| ecc={ecc} | ecc={ecc} | ||||
| theme={theme} | |||||
| route="/configure" | route="/configure" | ||||
| />, | />, | ||||
| ); | ); | ||||
| // Wait for wallet to load | // Wait for wallet to load | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Cashtab Loading'), | screen.queryByTitle('Cashtab Loading'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // Choose GBP | // Choose GBP | ||||
| await user.selectOptions( | await user.selectOptions( | ||||
| screen.getByTestId('configure-fiat-select'), | screen.getByTestId('configure-fiat-select'), | ||||
| screen.getByTestId('gbp'), | screen.getByTestId('gbp'), | ||||
| ); | ); | ||||
| // We expect balance header to be updated | // We expect balance header to be updated | ||||
| expect(screen.getByTitle('Price in Local Currency')).toHaveTextContent( | expect(screen.getByTitle('Price in Local Currency')).toHaveTextContent( | ||||
| `1 XEC = 0.00002000 GBP`, | `1 XEC = 0.00002000 GBP`, | ||||
| ); | ); | ||||
| // We expect localforage to be updated | // We expect localforage to be updated | ||||
| expect((await localforage.getItem('settings')).fiatCurrency).toEqual( | expect( | ||||
| 'gbp', | ((await localforage.getItem('settings')) as any).fiatCurrency, | ||||
| ); | ).toEqual('gbp'); | ||||
| }); | }); | ||||
| }); | }); | ||||