From bb7946b4033e65c4f7e7bf0b10ee791aba767645 Mon Sep 17 00:00:00 2001 From: Memas Deligeorgakis Date: Tue, 9 Aug 2022 15:03:59 +0200 Subject: [PATCH] #10 Unit tests for actions and configure in CI (#36) * Add some unit tests to shielded account actions * Configure to run in CI * Adjusted a failing test to reflect a change in a dependency * Removed a hard-coded value that was used during debugging * Adjusted setting of the state based on the change in the type of actions result * Changed the faulty test file name based on PR review * removed unnecessary import * Investigating a weird issue where the paths resolve locally but not in CI * corrected the paths after renaming --- .github/workflows/deploy-wallet-at-pr.yml | 39 +++ packages/anoma-wallet/package.json | 8 + .../Steps/Completion/Completion.tsx | 2 +- .../src/App/AccountOverview/AddAccount.tsx | 4 +- .../DerivedAccounts/DerivedAccounts.tsx | 2 +- .../src/App/Token/TokenDetails.tsx | 2 +- .../anoma-wallet/src/lib/tx/Transfer.test.ts | 2 +- .../{accountsNew => AccountsNew}/README.md | 0 .../__tests__/AccountsNew.actions.test.ts | 257 ++++++++++++++++++ .../AccountsNew/__tests__/AccountsNew.test.ts | 5 + .../{accountsNew => AccountsNew}/actions.ts | 28 +- .../{accountsNew => AccountsNew}/index.ts | 0 .../{accountsNew => AccountsNew}/reducers.ts | 2 +- .../{accountsNew => AccountsNew}/types.ts | 5 + packages/anoma-wallet/src/slices/accounts.ts | 2 +- packages/anoma-wallet/src/slices/transfers.ts | 2 +- packages/anoma-wallet/src/store/mocks.ts | 217 +++++++++++++++ packages/anoma-wallet/src/store/store.ts | 4 +- packages/masp-web/src/utils/masp-web/index.ts | 1 + yarn.lock | 26 ++ 20 files changed, 590 insertions(+), 18 deletions(-) rename packages/anoma-wallet/src/slices/{accountsNew => AccountsNew}/README.md (100%) create mode 100644 packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.actions.test.ts create mode 100644 packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.test.ts rename packages/anoma-wallet/src/slices/{accountsNew => AccountsNew}/actions.ts (81%) rename packages/anoma-wallet/src/slices/{accountsNew => AccountsNew}/index.ts (100%) rename packages/anoma-wallet/src/slices/{accountsNew => AccountsNew}/reducers.ts (96%) rename packages/anoma-wallet/src/slices/{accountsNew => AccountsNew}/types.ts (68%) create mode 100644 packages/anoma-wallet/src/store/mocks.ts create mode 100644 packages/masp-web/src/utils/masp-web/index.ts diff --git a/.github/workflows/deploy-wallet-at-pr.yml b/.github/workflows/deploy-wallet-at-pr.yml index 6d6489c36f8..e116bca9745 100644 --- a/.github/workflows/deploy-wallet-at-pr.yml +++ b/.github/workflows/deploy-wallet-at-pr.yml @@ -10,6 +10,45 @@ on: env: CI: false jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install dependencies + working-directory: ./packages/anoma-wallet + run: yarn + + - name: Install wasm-pack + uses: jetli/wasm-pack-action@v0.3.0 + with: + version: "v0.10.3" + + - name: Run unit tests + id: run-unit-tests + working-directory: ./packages/anoma-wallet + run: yarn test:coverage + env: + REACT_APP_ALIAS: "Namada Mainnet" + REACT_APP_CHAIN_ID: "anoma-masp-1.5.32ccad5356012a7" + REACT_APP_LEDGER_URL: "https://d3brk13lbhxfdb.cloudfront.net/anoma-masp-1.5.32ccad5356012a7" + REACT_APP_FAUCET: "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3" + + - name: report success + if: steps.run-unit-tests.outcome == 'success' + run: | + curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"message":"Unit tests succeeded ✅\n \n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n \nReview\nhttps://pull-request-${{ github.event.number }}--wallet-development-heliax-dev.netlify.app\n \nthe PR\nhttps://github.com/anoma/namada-interface/pull/${{ github.event.number }}"}' \ + ${{ secrets.SLACK_WEBHOOK_WALLET_PR }} + - name: report failure + if: steps.run-unit-tests.outcome != 'success' + run: | + curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"message":"Unit tests failed ⛔️ \n \n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n \nReview\nhttps://pull-request-${{ github.event.number }}--wallet-development-heliax-dev.netlify.app\n \nthe PR\nhttps://github.com/anoma/namada-interface/pull/${{ github.event.number }}"}' \ + ${{ secrets.SLACK_WEBHOOK_WALLET_PR }} build: runs-on: ubuntu-latest steps: diff --git a/packages/anoma-wallet/package.json b/packages/anoma-wallet/package.json index 2c5d1198b46..a38f13194f5 100644 --- a/packages/anoma-wallet/package.json +++ b/packages/anoma-wallet/package.json @@ -1,5 +1,10 @@ { "name": "@namada-interface/anoma-wallet", + "jest": { + "moduleNameMapper": { + "^@anoma/masp-web": "/../../node_modules/@anoma/masp-web" + } + }, "version": "0.1.0", "main": "./src/index.ts", "type": "commonjs", @@ -48,6 +53,7 @@ "wasm:build-in-ci": "./scripts/build.sh", "wasm:build:node": "./scripts/build-test.sh", "test": "yarn wasm:build:node && npx create-react-scripts test", + "test:coverage": "yarn test --coverage --watchAll=false", "e2e-test": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test", "e2e-test:headed": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test --project=chromium --headed", "eject": "npx react-scripts eject" @@ -83,6 +89,7 @@ "@types/react": "^17.0.39", "@types/react-dom": "^17.0.11", "@types/react-qr-reader": "^2.1.4", + "@types/redux-mock-store": "^1.0.3", "@types/styled-components": "^5.1.22", "babel-plugin-styled-components": "^2.0.3", "create-react-scripts": "^0.1.6", @@ -97,6 +104,7 @@ "history": "^5.3.0", "jest-fetch-mock": "^3.0.3", "prettier": "^2.5.1", + "redux-mock-store": "^1.5.4", "ts-loader": "^9.2.7" } } \ No newline at end of file diff --git a/packages/anoma-wallet/src/App/AccountCreation/Steps/Completion/Completion.tsx b/packages/anoma-wallet/src/App/AccountCreation/Steps/Completion/Completion.tsx index 66afb79b982..47be1a56a82 100644 --- a/packages/anoma-wallet/src/App/AccountCreation/Steps/Completion/Completion.tsx +++ b/packages/anoma-wallet/src/App/AccountCreation/Steps/Completion/Completion.tsx @@ -14,7 +14,7 @@ import { BodyText, } from "./Completion.components"; import { Tokens, TokenType } from "constants/"; -import { createShieldedAccount } from "slices/accountsNew/actions"; +import { createShieldedAccount } from "slices/AccountsNew/actions"; import { useNavigate } from "react-router-dom"; import { TopLevelRoute } from "App/types"; diff --git a/packages/anoma-wallet/src/App/AccountOverview/AddAccount.tsx b/packages/anoma-wallet/src/App/AccountOverview/AddAccount.tsx index e811055553c..dc20368127c 100644 --- a/packages/anoma-wallet/src/App/AccountOverview/AddAccount.tsx +++ b/packages/anoma-wallet/src/App/AccountOverview/AddAccount.tsx @@ -8,8 +8,8 @@ import { useAppDispatch, useAppSelector } from "store"; import { DerivedAccount, AccountsState, addAccount } from "slices/accounts"; import { SettingsState } from "slices/settings"; -import { NewAccountDetails } from "slices/accountsNew"; -import { createShieldedAccount, reset } from "slices/accountsNew/actions"; +import { NewAccountDetails } from "slices/AccountsNew"; +import { createShieldedAccount, reset } from "slices/AccountsNew/actions"; import { Label } from "components/Input/input.components"; import { Toggle } from "components/Toggle"; diff --git a/packages/anoma-wallet/src/App/AccountOverview/DerivedAccounts/DerivedAccounts.tsx b/packages/anoma-wallet/src/App/AccountOverview/DerivedAccounts/DerivedAccounts.tsx index bd3615cf98b..1b69d2cad3d 100644 --- a/packages/anoma-wallet/src/App/AccountOverview/DerivedAccounts/DerivedAccounts.tsx +++ b/packages/anoma-wallet/src/App/AccountOverview/DerivedAccounts/DerivedAccounts.tsx @@ -7,7 +7,7 @@ import { useAppDispatch, useAppSelector } from "store"; import { AccountsState } from "slices/accounts"; import { BalancesState, fetchBalances } from "slices/balances"; import { SettingsState } from "slices/settings"; -import { updateShieldedBalances } from "slices/accountsNew"; +import { updateShieldedBalances } from "slices/AccountsNew"; import { Symbols, Tokens, TokenType } from "constants/"; import { formatRoute } from "utils/helpers"; import { TransfersState } from "slices/transfers"; diff --git a/packages/anoma-wallet/src/App/Token/TokenDetails.tsx b/packages/anoma-wallet/src/App/Token/TokenDetails.tsx index 0664e434598..aecd18a223d 100644 --- a/packages/anoma-wallet/src/App/Token/TokenDetails.tsx +++ b/packages/anoma-wallet/src/App/Token/TokenDetails.tsx @@ -12,7 +12,7 @@ import { } from "slices/accounts"; import { TransfersState } from "slices/transfers"; import { SettingsState } from "slices/settings"; -import { updateShieldedBalances } from "slices/accountsNew"; +import { updateShieldedBalances } from "slices/AccountsNew"; import { useAppDispatch, useAppSelector } from "store"; import { formatRoute, stringFromTimestamp } from "utils/helpers"; import { ChainsState } from "slices/chains"; diff --git a/packages/anoma-wallet/src/lib/tx/Transfer.test.ts b/packages/anoma-wallet/src/lib/tx/Transfer.test.ts index a430bb046b3..cd55d4497e5 100644 --- a/packages/anoma-wallet/src/lib/tx/Transfer.test.ts +++ b/packages/anoma-wallet/src/lib/tx/Transfer.test.ts @@ -38,6 +38,6 @@ describe("Transfer wasm and class methods", () => { }); expect(hash.length).toBe(64); - expect(bytes.length).toBe(596); + expect(bytes.length).toBe(597); }); }); diff --git a/packages/anoma-wallet/src/slices/accountsNew/README.md b/packages/anoma-wallet/src/slices/AccountsNew/README.md similarity index 100% rename from packages/anoma-wallet/src/slices/accountsNew/README.md rename to packages/anoma-wallet/src/slices/AccountsNew/README.md diff --git a/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.actions.test.ts b/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.actions.test.ts new file mode 100644 index 00000000000..51d3b2b754e --- /dev/null +++ b/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.actions.test.ts @@ -0,0 +1,257 @@ +import { jest } from "@jest/globals"; +import { AnyAction } from "redux"; +import createMockStore from "redux-mock-store"; +import thunk, { ThunkDispatch } from "redux-thunk"; +import { RootState } from "store/store"; +import { mockAppState } from "store/mocks"; +import { updateShieldedBalances } from "../actions"; +import { ShieldedAccount } from "slices/accounts"; +import { AccountErrors } from "../types"; + +// imports containing mocked units +import * as shieldedTransfer from "slices/shieldedTransfer"; +jest.mock("slices/shieldedTransfer"); + +type DispatchExts = ThunkDispatch; +const middleware = [thunk]; +const mockStore = createMockStore(middleware); +const store = mockStore(mockAppState); + +describe("shielded balances", () => { + describe("fetchContactPageContent", () => { + beforeEach(() => { + store.clearActions(); + }); + + // this is not a focused case, but a very generic one just testing a happy path + // of updateShieldedBalances. + it("should behave as expected in happy path of updateShieldedBalances", async () => { + // setup data + const testChainId1 = "testChainId1"; + const testspendingKey1 = "testspendingKey1"; + const testBalanceAmount = 100; + const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1"; + const shieldedAccounts: { + [key: string]: { [key: string]: ShieldedAccount }; + } = { + testChainId1: { + [shieldedAccountUuidInClient]: { + chainId: "testChainId1", + shieldedKeysAndPaymentAddress: { + viewingKey: "viewingKey", + spendingKey: testspendingKey1, + paymentAddress: "paymentAddress", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "ETH", + signingKey: "signingKey", + publicKey: "publicKey", + id: shieldedAccountUuidInClient, + }, + }, + }; + // we create this here and set in the mocked function below to be able to assert that + // it was called with the right data + let expectedChainId; + let expectedSpendingKey; + + const store = mockStore({ + ...mockAppState, + settings: { ...mockAppState.settings, chainId: testChainId1 }, + accounts: { + ...mockAppState.accounts, + shieldedAccounts: shieldedAccounts, + }, + }); + // let's define a mock function that our action is calling + jest + .spyOn(shieldedTransfer, "getShieldedBalance") + .mockImplementation( + async ( + chainId: string, + inputAddress: string, + _tokenAddress: string + ): Promise => { + // we want to assert later that the correct values are being passed from the action + expectedChainId = chainId; + expectedSpendingKey = inputAddress; + + // we return the test balance amount + return Promise.resolve(`${testBalanceAmount}`); + } + ); + + // run unit under test + await store.dispatch(updateShieldedBalances()); + + // assert results + // ensure that the parameter was correctly passed from action to a dependency + expect(expectedChainId).toEqual(testChainId1); + expect(expectedSpendingKey).toEqual(testspendingKey1); + + // then ensure that the actions was resolving with expected results + const actionsReceivedByStore = await store.getActions(); + expect(actionsReceivedByStore[0].type).toEqual( + updateShieldedBalances.pending.type + ); + expect(actionsReceivedByStore[1].type).toEqual( + updateShieldedBalances.fulfilled.type + ); + + // lets get the returned balance of the account from the payload + const balanceOfShieldedAccountUuidInClient = + actionsReceivedByStore[1].payload.shieldedBalances[ + shieldedAccountUuidInClient + ]; + expect(balanceOfShieldedAccountUuidInClient).toEqual(testBalanceAmount); + expect(balanceOfShieldedAccountUuidInClient).not.toEqual( + `${testBalanceAmount}` + ); + }); + + it("should behave as expected in faulty response from wasm call in updateShieldedBalances", async () => { + // setup data + const testChainId1 = "testChainId1"; + const testBalanceAmount = "aaa"; + const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1"; + const shieldedAccounts: { + [key: string]: { [key: string]: ShieldedAccount }; + } = { + testChainId1: { + [shieldedAccountUuidInClient]: { + chainId: testChainId1, + shieldedKeysAndPaymentAddress: { + viewingKey: "viewingKey", + spendingKey: "testspendingKey1", + paymentAddress: "paymentAddress", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "ETH", + signingKey: "signingKey", + publicKey: "publicKey", + id: shieldedAccountUuidInClient, + }, + }, + }; + // we create this here and set in the mocked function below to be able to assert that + // it was called with the right data + + const store = mockStore({ + ...mockAppState, + settings: { ...mockAppState.settings, chainId: testChainId1 }, + accounts: { + ...mockAppState.accounts, + shieldedAccounts: shieldedAccounts, + }, + }); + // let's define a mock function that our action is calling + jest + .spyOn(shieldedTransfer, "getShieldedBalance") + .mockImplementation( + async ( + _chainId: string, + _inputAddress: string, + _tokenAddress: string + ): Promise => { + // we return the test balance amount + return Promise.resolve(`${testBalanceAmount}`); + } + ); + + // run unit under test + await store.dispatch(updateShieldedBalances()); + + // then ensure that the actions was resolving with expected results + const actionsReceivedByStore = await store.getActions(); + expect(actionsReceivedByStore[0].type).toEqual( + updateShieldedBalances.pending.type + ); + expect(actionsReceivedByStore[1].type).toEqual( + updateShieldedBalances.fulfilled.type + ); + + // lets get the returned balance of the account from the payload + const balanceOfShieldedAccountUuidInClient = + actionsReceivedByStore[1].payload.shieldedBalances[ + shieldedAccountUuidInClient + ]; + expect(balanceOfShieldedAccountUuidInClient).toEqual( + AccountErrors.NonNumericShieldedBalanceReturned + ); + }); + + it("should behave as expected when the action throws", async () => { + // setup data + const testChainId1 = "testChainId1"; + const testBalanceAmount = "aaa"; + const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1"; + const shieldedAccounts: { + [key: string]: { [key: string]: ShieldedAccount }; + } = { + [testChainId1 + "wrong_key"]: { + [shieldedAccountUuidInClient]: { + chainId: testChainId1, + shieldedKeysAndPaymentAddress: { + viewingKey: "viewingKey", + spendingKey: "testspendingKey1", + paymentAddress: "paymentAddress", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "ETH", + signingKey: "signingKey", + publicKey: "publicKey", + id: shieldedAccountUuidInClient, + }, + }, + }; + // we create this here and set in the mocked function below to be able to assert that + // it was called with the right data + + const store = mockStore({ + ...mockAppState, + settings: { ...mockAppState.settings, chainId: testChainId1 }, + accounts: { + ...mockAppState.accounts, + shieldedAccounts: shieldedAccounts, + }, + }); + // let's define a mock function that our action is calling + jest + .spyOn(shieldedTransfer, "getShieldedBalance") + .mockImplementation( + async ( + _chainId: string, + _inputAddress: string, + _tokenAddress: string + ): Promise => { + // we return the test balance amount + return Promise.resolve(`${testBalanceAmount}`); + } + ); + + // run unit under test + await store.dispatch(updateShieldedBalances()); + + // then ensure that the actions was resolving with expected results + const actionsReceivedByStore = await store.getActions(); + expect(actionsReceivedByStore[0].type).toEqual( + updateShieldedBalances.pending.type + ); + expect(actionsReceivedByStore[1].type).toEqual( + updateShieldedBalances.rejected.type + ); + expect(actionsReceivedByStore[1].payload).toEqual( + AccountErrors.RetrievingShieldedBalancesFailed + ); + }); + }); +}); diff --git a/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.test.ts b/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.test.ts new file mode 100644 index 00000000000..03c93bf4ecf --- /dev/null +++ b/packages/anoma-wallet/src/slices/AccountsNew/__tests__/AccountsNew.test.ts @@ -0,0 +1,5 @@ +import ReactTestUtils from "react-dom/test-utils"; + +describe("ShieldedTransfer", () => { + it.skip("testing the component's behavior", async () => {}); +}); diff --git a/packages/anoma-wallet/src/slices/accountsNew/actions.ts b/packages/anoma-wallet/src/slices/AccountsNew/actions.ts similarity index 81% rename from packages/anoma-wallet/src/slices/accountsNew/actions.ts rename to packages/anoma-wallet/src/slices/AccountsNew/actions.ts index 8ecb9edafaa..7d29b81ec40 100644 --- a/packages/anoma-wallet/src/slices/accountsNew/actions.ts +++ b/packages/anoma-wallet/src/slices/AccountsNew/actions.ts @@ -4,6 +4,7 @@ import { UPDATE_SHIELDED_BALANCES, NewAccountDetails, ShieldedAccount, + AccountErrors, } from "./types"; import { history, TopLevelRouteGenerator } from "App"; import { RootState } from "store/store"; @@ -76,12 +77,12 @@ export const createShieldedAccount = createAsyncThunk< export type ShieldedBalancesPayload = { chainId: string; shieldedBalances: { - [accountId: string]: number; + [accountId: string]: number | AccountErrors; }; }; export const updateShieldedBalances = createAsyncThunk< - ShieldedBalancesPayload | undefined, + ShieldedBalancesPayload, void >(UPDATE_SHIELDED_BALANCES, async (_, thunkAPI) => { try { @@ -93,7 +94,6 @@ export const updateShieldedBalances = createAsyncThunk< shieldedBalances: {}, }; - // TODO, is it good to have them all fail if one does, as now? for (const shieldedAccountId of Object.keys(shieldedAccounts)) { const shieldedAccount = shieldedAccounts[shieldedAccountId]; const { tokenType } = shieldedAccount; @@ -103,13 +103,27 @@ export const updateShieldedBalances = createAsyncThunk< shieldedAccount.shieldedKeysAndPaymentAddress.spendingKey, tokenAddress ); - // TODO unify the types and the location of conversions - shieldedBalances.shieldedBalances[shieldedAccountId] = - Number(shieldedBalance); + + // TODO, move the casting with errors to a common place + // we attempt to cast the balance to number + const shieldedBalanceAsNumber = Number(shieldedBalance); + // if the casting failed, we reassign the return value + if ( + typeof shieldedBalanceAsNumber === "number" && + isNaN(shieldedBalanceAsNumber) + ) { + shieldedBalances.shieldedBalances[shieldedAccountId] = + AccountErrors.NonNumericShieldedBalanceReturned; + } else { + shieldedBalances.shieldedBalances[shieldedAccountId] = + shieldedBalanceAsNumber; + } } return Promise.resolve(shieldedBalances); } catch (error) { - Promise.reject("error fetching shielded balances"); + return thunkAPI.rejectWithValue( + AccountErrors.RetrievingShieldedBalancesFailed + ); } }); diff --git a/packages/anoma-wallet/src/slices/accountsNew/index.ts b/packages/anoma-wallet/src/slices/AccountsNew/index.ts similarity index 100% rename from packages/anoma-wallet/src/slices/accountsNew/index.ts rename to packages/anoma-wallet/src/slices/AccountsNew/index.ts diff --git a/packages/anoma-wallet/src/slices/accountsNew/reducers.ts b/packages/anoma-wallet/src/slices/AccountsNew/reducers.ts similarity index 96% rename from packages/anoma-wallet/src/slices/accountsNew/reducers.ts rename to packages/anoma-wallet/src/slices/AccountsNew/reducers.ts index 632881c9f79..1f032ab6b65 100644 --- a/packages/anoma-wallet/src/slices/accountsNew/reducers.ts +++ b/packages/anoma-wallet/src/slices/AccountsNew/reducers.ts @@ -59,7 +59,7 @@ export const addAccountReducersToBuilder = ( const shieldedAccounts = state.shieldedAccounts[chainId]; for (const [key, shieldedBalance] of Object.entries(shieldedBalances)) { - if (shieldedAccounts[key]) { + if (shieldedAccounts[key] && typeof shieldedBalance === "number") { state.shieldedAccounts[chainId][key].balance = shieldedBalance; } } diff --git a/packages/anoma-wallet/src/slices/accountsNew/types.ts b/packages/anoma-wallet/src/slices/AccountsNew/types.ts similarity index 68% rename from packages/anoma-wallet/src/slices/accountsNew/types.ts rename to packages/anoma-wallet/src/slices/AccountsNew/types.ts index 30fc42f079a..c78719e5225 100644 --- a/packages/anoma-wallet/src/slices/accountsNew/types.ts +++ b/packages/anoma-wallet/src/slices/AccountsNew/types.ts @@ -13,3 +13,8 @@ export type ShieldedAccount = { spendingKey: string; paymentAddress: string; }; + +export enum AccountErrors { + NonNumericShieldedBalanceReturned = "AccountErrors.NonNumericShieldedBalanceReturned", + RetrievingShieldedBalancesFailed = "AccountErrors.RetrievingShieldedBalancesFailed", +} diff --git a/packages/anoma-wallet/src/slices/accounts.ts b/packages/anoma-wallet/src/slices/accounts.ts index a1c4d55bf08..f99cd18d35c 100644 --- a/packages/anoma-wallet/src/slices/accounts.ts +++ b/packages/anoma-wallet/src/slices/accounts.ts @@ -5,7 +5,7 @@ import { Account, RpcClient, SocketClient } from "lib"; import { NewBlockEvents } from "lib/rpc/types"; import { promiseWithTimeout, stringToHash } from "utils/helpers"; import { submitTransferTransaction } from "./transfers"; -import { addAccountReducersToBuilder } from "./accountsNew/reducers"; +import { addAccountReducersToBuilder } from "./AccountsNew/reducers"; export type InitialAccount = { chainId: string; diff --git a/packages/anoma-wallet/src/slices/transfers.ts b/packages/anoma-wallet/src/slices/transfers.ts index 898b5059386..0bec3efcd4b 100644 --- a/packages/anoma-wallet/src/slices/transfers.ts +++ b/packages/anoma-wallet/src/slices/transfers.ts @@ -20,7 +20,7 @@ import { createShieldedTransfer, TRANSFER_CONFIGURATION, } from "./shieldedTransfer"; -import { updateShieldedBalances } from "./accountsNew"; +import { updateShieldedBalances } from "./AccountsNew"; const TRANSFERS_ACTIONS_BASE = "transfers"; const LEDGER_TRANSFER_TIMEOUT = 20000; diff --git a/packages/anoma-wallet/src/store/mocks.ts b/packages/anoma-wallet/src/store/mocks.ts new file mode 100644 index 00000000000..8d4a95d4184 --- /dev/null +++ b/packages/anoma-wallet/src/store/mocks.ts @@ -0,0 +1,217 @@ +import { RootState } from "./store"; +import { TransferType } from "slices/transfers"; + +export const mockAppState: RootState = { + accounts: { + derived: { + "anoma-masp-1.5.32ccad5356012a7": { + "12RF8L": { + id: "12RF8L", + isInitial: true, + chainId: "anoma-masp-1.5.32ccad5356012a7", + alias: "Namada", + tokenType: "NAM", + address: "KwjdSzkCdzzsF983n8CgHnqcztVSQVLo3LJMUvkFzaAF3acar8e7", + publicKey: + "267ab5211a716883f630ac294cb30fd97fba9eb122b31b4bd3b72944931b307a", + signingKey: + "0f64215638e63f8f687b12ba11824763e700536b95ed7e58a3ec11598f1e9342", + balance: 0, + establishedAddress: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + isInitializing: false, + }, + }, + "anoma-test.1e670ba91369ec891fc": { + "39UL18": { + id: "39UL18", + isInitial: true, + chainId: "anoma-test.1e670ba91369ec891fc", + alias: "Namada", + tokenType: "NAM", + address: "L3iUoUYZ8NN3dBBAQeJbtJem4FoAznRvGvNLEuGGcKFMRnr4FbKj", + publicKey: + "6138f3a79b14854a2c69c64c8029d6eed1460bbfe4995f26185fa538740cb3dc", + signingKey: + "c1d30a9b550581cf14a4db3db9d7abeeebcb06d81737ef19b772f082d5b96c02", + }, + }, + "anoma-test.89060614ce340f4baae": { + "2MLGVA": { + id: "2MLGVA", + isInitial: true, + chainId: "anoma-test.89060614ce340f4baae", + alias: "Namada", + tokenType: "NAM", + address: "L1qDtV8TRwYLSHdMDW518hgRw9nWnRjFTenkcBYNJruyYoLjaj8F", + publicKey: + "17f47ab28c162729425dd01baebccadeb536c81ccd2c145a046239a9588eeef5", + signingKey: + "899e4dc8dac72c65dd3c61678b013ddd9a93f6f9f8626bce3c6f3ffc267f322e", + }, + }, + }, + shieldedAccounts: { + "anoma-test.89060614ce340f4baae": { + "30e4d7e8-ba39-459e-86cd-305249ddc169": { + chainId: "anoma-test.89060614ce340f4baae", + shieldedKeysAndPaymentAddress: { + viewingKey: + "xfvktest1qqqqqqqqqqqqqqzs4yx750l0n9sw7c9vsyl3ftrmhxgyxpz4l6v4jafvwlhkrj5y0x6fvvsp2krqkgpec7m9ayf9464ns69lwtfh8ukd0lqqtruwrjnsq24takm2jx9d4r6af3lc03e8y3vle23wepsfjxq85z903s2j0dx8wznwxjstqqc2pan27ggkhw86rg5ehug4s564m6hk6y0j7y3psq6y30zvlsr48nd239epjzq6czn39hst03awp3ppfgl440waautac8chmge4a", + spendingKey: + "xsktest1qqqqqqqqqqqqqqzs4yx750l0n9sw7c9vsyl3ftrmhxgyxpz4l6v4jafvwlhkrj5y09fxdrasqyk6q8w7r9mt340m3mjqtuveutg6kmfcgh7u72yyneksusxkzm58p484x0mufldsukq7l7kaj0lej2cnaqylpkn8eukecls9wznwxjstqqc2pan27ggkhw86rg5ehug4s564m6hk6y0j7y3psq6y30zvlsr48nd239epjzq6czn39hst03awp3ppfgl440waautac8ck65jc0", + paymentAddress: + "patest1merkqqffspqakum38hz2z9ghhmv4fhpunn0pt9vpwwpdcyqr7adq9xjjcfw84wuldy7sjqyh03u", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "NAM", + signingKey: "signingKey", + publicKey: "publicKey", + id: "30e4d7e8-ba39-459e-86cd-305249ddc169", + }, + }, + "anoma-masp-1.5.32ccad5356012a7": { + "11ce9d8b-414a-43dc-a6c5-dc0d2a0ef42b": { + chainId: "anoma-masp-1.5.32ccad5356012a7", + shieldedKeysAndPaymentAddress: { + viewingKey: + "xfvktest1qqqqqqqqqqqqqqqjknf8hm5ckx50eqzdmupgesqa7laf7e73nwcyez5yxsqlpgpdl34ucymtftg7v8ae57s4szzta9wpj83vyf6mjjehm8nvvvuchf0acxj8luey9ufd9zwccta0fvhw7e6595htc3w06yanae9t7dqj0npmrzkgfq95rtu3ekfpxc3vfnfwzfxgtqffyhpzjxfqnsh2dnu5t65uhha7gljymdurrykja7yk2l4tq0lsnww8z60szzkea6heugsptac0zna9f", + spendingKey: + "xsktest1qqqqqqqqqqqqqqqjknf8hm5ckx50eqzdmupgesqa7laf7e73nwcyez5yxsqlpgpdljx3qxkg60j0cpmva987rpw6nf2pja8p9p9jjuk4wg58g8h9ca7snmr5nuxt7fj83mypk3wfjmjf320uvnk6jy8hv0ylf6lykh70q8sgrzkgfq95rtu3ekfpxc3vfnfwzfxgtqffyhpzjxfqnsh2dnu5t65uhha7gljymdurrykja7yk2l4tq0lsnww8z60szzkea6heugsptac9elefp", + paymentAddress: + "patest1cswh6hx6zkw6pyhrf53rxespamutnj0l428q5qevkq9h680s6qlr85tw6hpme0ttzqhhywaul3u", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "NAM", + signingKey: "signingKey", + publicKey: "publicKey", + id: "11ce9d8b-414a-43dc-a6c5-dc0d2a0ef42b", + }, + }, + "anoma-test.1e670ba91369ec891fc": { + "81dc5a0f-1840-4f26-86fa-62b755eb6c4e": { + chainId: "anoma-test.1e670ba91369ec891fc", + shieldedKeysAndPaymentAddress: { + viewingKey: + "xfvktest1qqqqqqqqqqqqqqqvcs6945a2saw582e4fgddpu962ud89q9g6f9kyhn4mcn8z7cht4jx8zryykr59efqy0w2g0fnn7sh40dcqa76r9vs8ne6z2rk4tzhr8wwmdzue9us2sx3rqkvtj5u8wpaplttmjn89gjy56n98hcmaprqzznfzxylnk4j4mvahu3047u8k2jegwpmpx4khu30nrxdner5mztqjq2tx2pm2lclzewdwkhjyqqxznvfxgpr5zx0g9ph6a7jqhfjdysktf7e9", + spendingKey: + "xsktest1qqqqqqqqqqqqqqqvcs6945a2saw582e4fgddpu962ud89q9g6f9kyhn4mcn8z7chtkrlqwes36jgfpe6x0fk9x5xdul4s8k8030l24e9ujuvx0ngw2wqhkf7jg2xgzfdwkv3y2tgsv0642xwjkvclhsth7w6vwejpd5l2tcfzznfzxylnk4j4mvahu3047u8k2jegwpmpx4khu30nrxdner5mztqjq2tx2pm2lclzewdwkhjyqqxznvfxgpr5zx0g9ph6a7jqhfjdysagtpg6", + paymentAddress: + "patest1804lh5v73k0e75n0sdyu0799e8hrz3zpnlxdc5e6wfdkpk4t7525llvdf2gf9ul03h8sj85v79r", + }, + isShielded: true, + alias: "Namada", + balance: 0, + address: "this_should_be_the_current_masp_address", + tokenType: "NAM", + signingKey: "signingKey", + publicKey: "publicKey", + id: "81dc5a0f-1840-4f26-86fa-62b755eb6c4e", + }, + }, + }, + isAddingAccountInReduxState: false, + }, + balances: { + "anoma-masp-1.5.32ccad5356012a7": { + "12RF8L": { + NAM: 1000, + ATOM: 1000, + ETH: 1000, + DOT: 0, + BTC: 1000, + }, + }, + }, + transfers: { + transactions: [ + { + chainId: "anoma-masp-1.5.32ccad5356012a7", + source: + "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3", + target: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + appliedHash: + "C90CE1D0FBF4562A01207C9C126A401C64D9CC6D2203A8D219E6A9EF645F9F0E", + tokenType: "NAM", + amount: 1000, + memo: "Initial funds", + gas: 1.232945, + height: 226619, + timestamp: 1659444390179, + type: TransferType.NonShielded, + }, + { + chainId: "anoma-masp-1.5.32ccad5356012a7", + source: + "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3", + target: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + appliedHash: + "C90CE1D0FBF4562A01207C9C126A401C64D9CC6D2203A8D219E6A9EF645F9F0E", + tokenType: "ETH", + amount: 1000, + memo: "Initial funds", + gas: 1.232945, + height: 226619, + timestamp: 1659444390683, + type: TransferType.NonShielded, + }, + { + chainId: "anoma-masp-1.5.32ccad5356012a7", + source: + "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3", + target: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + appliedHash: + "C90CE1D0FBF4562A01207C9C126A401C64D9CC6D2203A8D219E6A9EF645F9F0E", + tokenType: "ATOM", + amount: 1000, + memo: "Initial funds", + gas: 1.232945, + height: 226619, + timestamp: 1659444390845, + type: TransferType.NonShielded, + }, + { + chainId: "anoma-masp-1.5.32ccad5356012a7", + source: + "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3", + target: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + appliedHash: + "C90CE1D0FBF4562A01207C9C126A401C64D9CC6D2203A8D219E6A9EF645F9F0E", + tokenType: "BTC", + amount: 1000, + memo: "Initial funds", + gas: 1.232945, + height: 226619, + timestamp: 1659444391098, + type: TransferType.NonShielded, + }, + ], + isTransferSubmitting: false, + isIbcTransferSubmitting: false, + transferError: + "Async actions timed out when submitting Token Transfer after 20 seconds", + }, + channels: { + channelsByChain: { + "anoma-test.1e670ba91369ec891fc": ["channel-0"], + "anoma-test.89060614ce340f4baae": ["channel-0"], + }, + }, + settings: { + fiatCurrency: "USD", + chainId: "anoma-masp-1.5.32ccad5356012a7", + }, + coins: { + rates: {}, + }, +}; diff --git a/packages/anoma-wallet/src/store/store.ts b/packages/anoma-wallet/src/store/store.ts index 70c5df00661..51d4ab89ce3 100644 --- a/packages/anoma-wallet/src/store/store.ts +++ b/packages/anoma-wallet/src/store/store.ts @@ -21,9 +21,9 @@ import { LocalStorageKeys } from "App/types"; import { hashPassword } from "utils/helpers"; const reducers = combineReducers({ - accounts: accountsReducer, + accounts: accountsReducer || {}, balances: balancesReducer, - transfers: transfersReducer, + transfers: transfersReducer || {}, channels: channelsReducer, settings: settingsReducer, coins: coinsReducer, diff --git a/packages/masp-web/src/utils/masp-web/index.ts b/packages/masp-web/src/utils/masp-web/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/packages/masp-web/src/utils/masp-web/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/yarn.lock b/yarn.lock index e3a99405a09..534cfc024ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2083,6 +2083,13 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/redux-mock-store@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz#895de4a364bc4836661570aec82f2eef5989d1fb" + integrity sha512-Wqe3tJa6x9MxMN4DJnMfZoBRBRak1XTPklqj4qkVm5VBpZnC8PSADf4kLuFQ9NAdHaowfWoEeUMz7NWc2GMtnA== + dependencies: + redux "^4.0.5" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -6542,6 +6549,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -8261,6 +8273,13 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redux-mock-store@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872" + integrity sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA== + dependencies: + lodash.isplainobject "^4.0.6" + redux-persist-transform-encrypt@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/redux-persist-transform-encrypt/-/redux-persist-transform-encrypt-3.0.1.tgz#d9428a649a6eefa69f88f61ed9d846f4b8ea0d5b" @@ -8286,6 +8305,13 @@ redux@^4.0.0, redux@^4.1.2: dependencies: "@babel/runtime" "^7.9.2" +redux@^4.0.5: + version "4.2.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13" + integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA== + dependencies: + "@babel/runtime" "^7.9.2" + regenerate-unicode-properties@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"