From 416bdd334df3aec7fcf7df89ae5d825cb1004fab Mon Sep 17 00:00:00 2001 From: bgptr Date: Sun, 2 Jul 2023 12:39:39 +0200 Subject: [PATCH] determine nextAddressAccount from getNextAddressResponse rather than from the selector. Thus decreasing interference of the tests. --- .../inputs/ReceiveAccountsSelect.jsx | 12 +- .../shared/SendTransaction/hooks.js | 12 +- app/selectors.js | 5 +- .../views/LNPage/ConnectPage.spec.js | 22 +- .../views/PrivacyPage/PrivacyTab.spec.js | 91 +++++++- .../HistoryTab/HistoryTab.spec.js | 39 ++-- .../ReceiveTab/ReceiveTab.spec.js | 55 ++++- .../TransactionsPage/SendTab/SendTab.spec.js | 202 ++++++++++++++---- 8 files changed, 356 insertions(+), 82 deletions(-) diff --git a/app/components/inputs/ReceiveAccountsSelect.jsx b/app/components/inputs/ReceiveAccountsSelect.jsx index b56b8f8399..f090b8ebbe 100644 --- a/app/components/inputs/ReceiveAccountsSelect.jsx +++ b/app/components/inputs/ReceiveAccountsSelect.jsx @@ -3,6 +3,7 @@ import AccountsSelect from "./AccountsSelect"; import { useSelector, useDispatch } from "react-redux"; import * as ca from "actions/ControlActions"; import * as sel from "selectors"; +import { compose, get, eq } from "lodash/fp"; function ReceiveAccountsSelect({ onChange, @@ -12,7 +13,16 @@ function ReceiveAccountsSelect({ }) { const dispatch = useDispatch(); const mixedAccount = useSelector(sel.getMixedAccount); - const nextAddressAccount = useSelector(sel.nextAddressAccount); + const getNextAddressResponse = useSelector(sel.getNextAddressResponse); + const visibleAccounts = useSelector(sel.visibleAccounts); + + const nextAddressAccountNumber = getNextAddressResponse + ? getNextAddressResponse.accountNumber + : null; + + const nextAddressAccount = visibleAccounts.find( + compose(eq(nextAddressAccountNumber), get("value")) + ); const getNextAddressAttempt = useCallback( (value) => dispatch(ca.getNextAddressAttempt(value)), diff --git a/app/components/shared/SendTransaction/hooks.js b/app/components/shared/SendTransaction/hooks.js index 4a8d45eb3c..8ca1f06541 100644 --- a/app/components/shared/SendTransaction/hooks.js +++ b/app/components/shared/SendTransaction/hooks.js @@ -4,6 +4,7 @@ import * as ca from "actions/ControlActions"; import { baseOutput } from "./helpers"; import { useSelector, useDispatch, shallowEqual } from "react-redux"; import { usePrevious } from "hooks"; +import { compose, get, eq } from "lodash/fp"; export function useSendTransaction() { const defaultSpendingAccount = useSelector( @@ -16,7 +17,16 @@ export function useSendTransaction() { const estimatedSignedSize = useSelector(sel.estimatedSignedSize); const totalSpent = useSelector(sel.totalSpent); const nextAddress = useSelector(sel.nextAddress); - const nextAddressAccount = useSelector(sel.nextAddressAccount, shallowEqual); + const getNextAddressResponse = useSelector(sel.getNextAddressResponse); + const visibleAccounts = useSelector(sel.visibleAccounts); + + const nextAddressAccountNumber = getNextAddressResponse + ? getNextAddressResponse.accountNumber + : null; + + const nextAddressAccount = visibleAccounts.find( + compose(eq(nextAddressAccountNumber), get("value")) + ); const constructTxLowBalance = useSelector(sel.constructTxLowBalance); const publishTxResponse = useSelector(sel.publishTxResponse); const notMixedAccounts = useSelector( diff --git a/app/selectors.js b/app/selectors.js index 8a287b5591..cd617f491e 100644 --- a/app/selectors.js +++ b/app/selectors.js @@ -717,7 +717,10 @@ export const buyerAccount = createSelector( ); export const buyerMaxFeePercentage = get(["vsp", "maxFeePercentage"]); -const getNextAddressResponse = get(["control", "getNextAddressResponse"]); +export const getNextAddressResponse = get([ + "control", + "getNextAddressResponse" +]); const nextAddressAccountNumber = compose( (res) => (res ? res.accountNumber : null), getNextAddressResponse diff --git a/test/unit/components/views/LNPage/ConnectPage.spec.js b/test/unit/components/views/LNPage/ConnectPage.spec.js index cfbb6e0f43..13a74ecade 100644 --- a/test/unit/components/views/LNPage/ConnectPage.spec.js +++ b/test/unit/components/views/LNPage/ConnectPage.spec.js @@ -163,7 +163,16 @@ test("test create new new account", async () => { }); test("test text toggles", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await goToCreateWalletView(user); expect(screen.queryByText(/attention:/i)).not.toBeInTheDocument(); @@ -194,7 +203,16 @@ test("test automatic channel creation", async () => { }); test("test use existing account", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await goToCreateWalletView(user); expect(screen.queryByText(/attention:/i)).not.toBeInTheDocument(); diff --git a/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js b/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js index 3c0aaa4376..1462b1d41b 100644 --- a/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js +++ b/test/unit/components/views/PrivacyPage/PrivacyTab.spec.js @@ -66,6 +66,12 @@ const mockSpendingAccounts = [ mockMixedAccount, mockUnMixedAccount ]; + +const mockVisibleAccounts = [ + mockDefaultAccount, + mockMixedAccount, + mockUnMixedAccount +]; const mockCsppServer = "mockCsppServer.decred.org"; const mockCsppPort = "1234"; const mockMixedAccountBranch = 0; @@ -83,6 +89,7 @@ let mockDispatchSingleMessage; beforeEach(() => { selectors.currencyDisplay = jest.fn(() => DCR); selectors.defaultSpendingAccount = jest.fn(() => mockMixedAccount); + selectors.visibleAccounts = jest.fn(() => mockVisibleAccounts); selectors.getPrivacyEnabled = jest.fn(() => true); selectors.isWatchingOnly = jest.fn(() => false); selectors.getMixedAccountName = jest.fn(() => mockMixedAccount.name); @@ -91,7 +98,6 @@ beforeEach(() => { selectors.walletService = jest.fn(() => { return {}; }); - selectors.nextAddressAccount = jest.fn(() => mockUnMixedAccount); selectors.nextAddress = jest.fn(() => mockNextAddress); selectors.getRunningIndicator = jest.fn(() => false); @@ -235,7 +241,16 @@ test("test insufficient unmixed account balance error message", async () => { (acc) => acc.value == acctId ) ); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockUnMixedAccount.value + } + } + } + }); + await waitFor(() => expect(screen.getByText("Unmixed Balance").parentNode.className).toMatch( /balanceError/i @@ -248,7 +263,16 @@ test("test insufficient unmixed account balance error message", async () => { }); test("start coin mixer", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await waitFor(() => expect( screen.getByText("Unmixed Balance").parentNode.className @@ -289,7 +313,15 @@ test("start coin mixer", async () => { test("stop coin mixer", async () => { selectors.getAccountMixerRunning = jest.fn(() => true); - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(screen.getByText("Mixer is running")).toBeInTheDocument(); await user.click(getStopMixerButton()); @@ -299,14 +331,30 @@ test("stop coin mixer", async () => { test("mixer is disabled (Autobuyer running)", () => { selectors.getRunningIndicator = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(getSendToSelfButton().disabled).toBe(true); expect(getStartMixerButton().disabled).toBe(true); }); test("allow sending from unmixed accounts", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const checkbox = getPrivacyCheckbox(); @@ -333,7 +381,15 @@ test("allow sending from unmixed accounts", async () => { test("sending from unmixed accounts is allowed already", async () => { selectors.getAllowSendFromUnmixed = jest.fn(() => true); - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const checkbox = getPrivacyCheckbox(); expect(checkbox.checked).toBe(true); @@ -343,7 +399,16 @@ test("sending from unmixed accounts is allowed already", async () => { }); test("Send to Unmixed Account form", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + const sendToSelfBtn = getSendToSelfButton(); const amountInput = screen.getByLabelText("Amount:"); const testAmount = "12"; @@ -372,7 +437,15 @@ test("check logs", async () => { mockGetPrivacyLogs = wallet.getPrivacyLogs = jest.fn(() => Promise.resolve(mockLogLine) ); - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const logsLabel = screen.getByText("Logs"); diff --git a/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js b/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js index 5740cd2934..feab5f0b0e 100644 --- a/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js +++ b/test/unit/components/views/TransactionsPage/HistoryTab/HistoryTab.spec.js @@ -13,23 +13,6 @@ let mockWalletService; const selectors = sel; const transactionActions = ta; -const initialState = { - grpc: { - transactionsFilter: { - search: null, - listDirection: "desc", - types: [], - directions: [], - maxAmount: null, - minAmount: null - }, - regularTransactions: {}, - getRegularTxsAux: { - noMoreTransactions: false - } - } -}; - const getTestTxs = (startTs) => { const txList = {}; const startDate = new Date(startTs * 1000); @@ -103,6 +86,28 @@ const mockTotalSpent = 5600005850; const mockEstimatedFee = 5850; const mockEstimatedSize = 585; +const initialState = { + grpc: { + transactionsFilter: { + search: null, + listDirection: "desc", + types: [], + directions: [], + maxAmount: null, + minAmount: null + }, + regularTransactions: {}, + getRegularTxsAux: { + noMoreTransactions: false + } + }, + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } +}; + beforeEach(() => { selectors.isTestNet = jest.fn(() => false); selectors.isMainNet = jest.fn(() => false); diff --git a/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js b/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js index 34aec0c976..2d0d186c02 100644 --- a/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js +++ b/test/unit/components/views/TransactionsPage/ReceiveTab/ReceiveTab.spec.js @@ -135,7 +135,16 @@ const getQRCodeButton = () => screen.getByText("QR code").nextElementSibling; const getModalCloseButton = () => screen.getByText("Close"); test("render ReceiveTab within its parent", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await user.click(screen.getByText("Receive")); expect( @@ -146,7 +155,16 @@ test("render ReceiveTab within its parent", async () => { }); test("change destination account", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + selectors.nextAddressAccount = jest.fn(() => mockAccount2); expect(screen.getByText(mockNextAddress)).toBeInTheDocument(); @@ -162,7 +180,16 @@ test("change destination account", async () => { }); test("generate new address", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await user.click(screen.getByText(/generate new address/i)); expect(mockGetNextAddressAttempt).toHaveBeenCalled(); }); @@ -181,13 +208,31 @@ test("show error when there is no walletService", () => { }); test("test copy button", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await user.click(getCopyButton()); expect(mockCopy).toHaveBeenCalledWith(mockNextAddress); }); test("test QR button", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + await user.click(getQRCodeButton()); expect(mockGenQRCodeSVG).toHaveBeenCalledWith(`decred:${mockNextAddress}`); expect(screen.getAllByText(mockNextAddress).length).toBe(2); // + modal diff --git a/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js b/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js index ac068e453f..dc1338cfaf 100644 --- a/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js +++ b/test/unit/components/views/TransactionsPage/SendTab/SendTab.spec.js @@ -96,7 +96,6 @@ let mockIsMainNet; let mockWalletService; let mockConstructTransactionAttempt; let mockValidateAddress; -let mockGetNextAddressAttempt; const selectors = sel; const controlActions = ca; @@ -120,9 +119,7 @@ beforeEach(() => { selectors.estimatedSignedSize = jest.fn(() => mockEstimatedSize); selectors.totalSpent = jest.fn(() => mockTotalSpent); selectors.nextAddress = jest.fn(() => mockNextAddress); - selectors.nextAddressAccount = jest.fn(() => mockDefaultAccount); selectors.constructTxLowBalance = jest.fn(() => false); - selectors.publishTxResponse = jest.fn(() => "mockpublishtxresponse"); selectors.getNotMixedAcctIfAllowed = jest.fn(() => [0, 2]); selectors.isTrezor = jest.fn(() => false); selectors.isWatchingOnly = jest.fn(() => false); @@ -146,19 +143,17 @@ beforeEach(() => { }; }); controlActions.onClearTransaction = jest.fn(() => {}); - mockGetNextAddressAttempt = controlActions.getNextAddressAttempt = jest.fn( - () => (dispatch) => { - const res = { - address: "mock-next-address", - accountNumber: mockAccount2.value - }; - dispatch({ - getNextAddressResponse: res, - type: GETNEXTADDRESS_SUCCESS - }); - Promise.resolve(res); - } - ); + controlActions.getNextAddressAttempt = jest.fn(() => (dispatch) => { + const res = { + address: "mock-next-address", + accountNumber: mockAccount2.value + }; + dispatch({ + getNextAddressResponse: res, + type: GETNEXTADDRESS_SUCCESS + }); + Promise.resolve(res); + }); transactionActions.listUnspentOutputs = jest.fn( () => () => Promise.resolve(mockUnspentOutputs) ); @@ -203,7 +198,15 @@ const getAllAmountInput = () => screen.getAllByLabelText("Amount"); const getAllSendToInput = () => screen.getAllByLabelText("Send to"); test("render SendTab within its parent", () => { - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const amountInput = getAmountInput(); const sendToInput = getSendToInput(); @@ -251,7 +254,15 @@ test("render SendTab within its parent", () => { test("render SendTab within its parent in testnet mode", () => { mockIsTestNet = selectors.isTestNet = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); expect(screen.getByText(/testnet Decred addresses/i).textContent) .toMatchInlineSnapshot(` @@ -261,7 +272,15 @@ test("render SendTab within its parent in testnet mode", () => { }); test("test amount input", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); let amountInput = getAmountInput(); const sendAllButton = getSendAllButton(); @@ -359,7 +378,15 @@ test("test amount input", async () => { }); test("test `send to` input", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const amountInput = getAmountInput(); const sendToInput = getSendToInput(); @@ -403,7 +430,15 @@ test("test `send to` input", async () => { }); test("test paste button", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendToInput = getSendToInput(); const pasteButton = getPasteButton(); @@ -419,7 +454,15 @@ test("test paste button", async () => { }); test("test paste button (paste address with trailing and leading spaces)", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendToInput = getSendToInput(); const pasteButton = getPasteButton(); @@ -438,7 +481,15 @@ test("test paste button (paste address with trailing and leading spaces)", async }); test("type address with trailing and leading spaces", async () => { - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendToInput = getSendToInput(); const amountInput = getAmountInput(); @@ -457,7 +508,15 @@ test("type address with trailing and leading spaces", async () => { }); test("construct a valid transaction", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); selectors.unsignedTransaction = jest.fn(() => 1); mockValidateAddress = controlActions.validateAddress = jest.fn(() => () => { @@ -485,14 +544,31 @@ test("construct a valid transaction", async () => { test("test insufficient funds", () => { selectors.constructTxLowBalance = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(getSendButton().disabled).toBe(true); expect(screen.getByText(/insufficient funds/i)).toBeInTheDocument(); }); test("`Sending from unmixed account` is allowed", async () => { selectors.getNotMixedAcctIfAllowed = jest.fn(() => []); - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); await user.click(screen.getByText(mockMixedAccount.name)); expect(screen.getByText(mockDefaultAccount.name)).toBeInTheDocument(); @@ -560,7 +636,15 @@ const fillOutputForm = async (user, index) => { }; test("test sending to multiple addresses", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); mockValidateAddress = controlActions.validateAddress = jest.fn(() => () => { return { @@ -596,7 +680,15 @@ test("test sending to multiple addresses", async () => { }); test("send funds to another account", async () => { - const { user } = render(); + const { user } = render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); const sendSelfButton = getSendSelfButton(); @@ -604,17 +696,11 @@ test("send funds to another account", async () => { target: { value: `${validAmount}` } }); await user.click(sendSelfButton); - await user.click(screen.getAllByRole("combobox")[1]); - selectors.nextAddressAccount = jest.fn(() => mockAccount2); + await user.click(screen.getByText(mockDefaultAccount.name)); await user.click(screen.getByText(mockAccount2.name)); - await waitFor( - () => expect(mockGetNextAddressAttempt).toHaveBeenCalled(), - 5000 - ); await waitFor(() => - expect(screen.queryByText(mockDefaultAccount.name)).not.toBeInTheDocument() + expect(screen.getAllByText(mockAccount2.name).length).toBe(1) ); - selectors.publishTxResponse = jest.fn(() => "mocknewpublishtxresponse"); await waitFor(() => expect(mockConstructTransactionAttempt).toHaveBeenCalledWith( mockMixedAccountValue, @@ -623,19 +709,25 @@ test("send funds to another account", async () => { undefined ) ); - await waitFor(() => - expect(screen.queryByText(mockDefaultAccount.name)).not.toBeInTheDocument() - ); - expect(mockGetNextAddressAttempt).toHaveBeenCalled(); - // switch back from send to self mode await user.click(sendSelfButton); - expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument(); -}, 10000); + await waitFor(() => + expect(screen.queryByText(mockAccount2.name)).not.toBeInTheDocument() + ); +}); test("Privacy Mixer, Autobuyer or Purchase Ticket Attempt running", () => { selectors.getRunningIndicator = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(getSendButton().disabled).toBe(true); expect( screen.getByText( @@ -649,7 +741,16 @@ test("show unsigned raw transaction", () => { selectors.isWatchingOnly = jest.fn(() => true); selectors.isTrezor = jest.fn(() => false); selectors.unsignedRawTx = jest.fn(() => mockUnsignedRawTx); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(screen.getByText(/Unsigned Raw Transaction:/)).toBeInTheDocument(); expect(screen.getByText(mockUnsignedRawTx)).toBeInTheDocument(); }); @@ -657,6 +758,15 @@ test("show unsigned raw transaction", () => { test("watching only trezor should show send button", () => { selectors.isWatchingOnly = jest.fn(() => true); selectors.isTrezor = jest.fn(() => true); - render(); + render(, { + initialState: { + control: { + getNextAddressResponse: { + accountNumber: mockDefaultAccount.value + } + } + } + }); + expect(getSendButton()).toBeInTheDocument(); });