Skip to content

Commit

Permalink
Refactor removeAccountDataHook to ease the testing
Browse files Browse the repository at this point in the history
  • Loading branch information
asiia-trilitech committed Mar 14, 2024
1 parent 0c56a09 commit b13a5dd
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 110 deletions.
2 changes: 1 addition & 1 deletion src/components/AccountDrawer/RenameRemoveMenuSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
import { RenameAccountModal } from "./RenameAccountModal";
import { Account } from "../../types/Account";
import { useImplicitAccounts } from "../../utils/hooks/getAccountDataHooks";
import { useRemoveAccount } from "../../utils/hooks/removeAccountDataHooks";
import { useRemoveAccount } from "../../utils/hooks/setAccountDataHooks";
import { ConfirmationModal } from "../ConfirmationModal";
import { DynamicModalContext } from "../DynamicModal";
import { RenameRemoveMenu } from "../RenameRemoveMenu";
Expand Down
26 changes: 0 additions & 26 deletions src/utils/hooks/removeAccountDataHooks.test.ts

This file was deleted.

76 changes: 0 additions & 76 deletions src/utils/hooks/removeAccountDataHooks.ts

This file was deleted.

37 changes: 37 additions & 0 deletions src/utils/hooks/removeAccountDependenciesHooks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useRemoveAccountsDependencies } from "./removeAccountDependenciesHooks";
import { mockImplicitAccount, mockTezOperation } from "../../mocks/factories";
import { renderHook } from "../../mocks/testUtils";
import { makeAccountOperations } from "../../types/AccountOperations";
import { MAINNET } from "../../types/Network";
import { batchesActions } from "../redux/slices/batches";
import { store } from "../redux/store";

describe("useRemoveAccountsDependencies", () => {
it("removes batches related to the given accounts", () => {
const accountOperations1 = makeAccountOperations(
mockImplicitAccount(1),
mockImplicitAccount(1),
[mockTezOperation(0), mockTezOperation(1)]
);
const accountOperations2 = makeAccountOperations(
mockImplicitAccount(2),
mockImplicitAccount(2),
[mockTezOperation(1), mockTezOperation(3)]
);
const accountOperations3 = makeAccountOperations(
mockImplicitAccount(3),
mockImplicitAccount(3),
[mockTezOperation(0)]
);
store.dispatch(batchesActions.add({ operations: accountOperations1, network: MAINNET }));
store.dispatch(batchesActions.add({ operations: accountOperations2, network: MAINNET }));
store.dispatch(batchesActions.add({ operations: accountOperations3, network: MAINNET }));

const {
result: { current: removeAccountsDependencies },
} = renderHook(() => useRemoveAccountsDependencies());
removeAccountsDependencies([mockImplicitAccount(2), mockImplicitAccount(3)]);

expect(store.getState().batches[MAINNET.name]).toEqual([accountOperations1]);
});
});
18 changes: 18 additions & 0 deletions src/utils/hooks/removeAccountDependenciesHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Account } from "../../types/Account";
import { useAppDispatch } from "../redux/hooks";
import { batchesSlice } from "../redux/slices/batches";

/**
* Hook for removing all stored accounts' dependencies.
*
* That means:
* - removing all batches related to the given accounts
*/
export const useRemoveAccountsDependencies = () => {
const dispatch = useAppDispatch();

return (accounts: Account[]) => {
const pkhs = accounts.map(account => account.address.pkh);
dispatch(batchesSlice.actions.removeByAccounts({ pkhs }));
};
};
46 changes: 45 additions & 1 deletion src/utils/hooks/setAccountDataHooks.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { useDeriveMnemonicAccount, useRestoreFromMnemonic } from "./setAccountDataHooks";
import { useRemoveAccountsDependencies } from "./removeAccountDependenciesHooks";
import {
useDeriveMnemonicAccount,
useRemoveAccount,
useRestoreFromMnemonic,
} from "./setAccountDataHooks";
import { mockSecretKeyAccount, mockSocialAccount } from "../../mocks/factories";
import { fakeAddressExists } from "../../mocks/helpers";
import { mnemonic1 } from "../../mocks/mockMnemonic";
Expand All @@ -10,9 +15,17 @@ import { accountsSlice } from "../redux/slices/accountsSlice";
import { store } from "../redux/store";
import * as tezosHelpers from "../tezos/helpers";

jest.mock("./removeAccountDependenciesHooks");
jest.unmock("../tezos");

const mockedUseRemoveAccountsDependencies = jest.mocked(useRemoveAccountsDependencies);
const mockedRemoveAccountsDependencies = jest.fn();

describe("setAccountDataHooks", () => {
beforeEach(() => {
mockedUseRemoveAccountsDependencies.mockReturnValue(mockedRemoveAccountsDependencies);
});

describe("mnemonic accounts", () => {
const addressExistsMock = jest.spyOn(tezosHelpers, "addressExists");
const getFingerPrintMock = jest.spyOn(tezosHelpers, "getFingerPrint");
Expand Down Expand Up @@ -286,4 +299,35 @@ describe("setAccountDataHooks", () => {
});
});
});

describe("useRemoveAccount", () => {
it("deletes secret key on deleting secret key account", () => {
const account = mockSecretKeyAccount(0);
store.dispatch(accountsSlice.actions.addAccount(account));
store.dispatch(
accountsSlice.actions.addSecretKey({
pkh: account.address.pkh,
encryptedSecretKey: "encryptedSecretKey" as any,
})
);

const {
result: { current: removeAccount },
} = renderHook(() => useRemoveAccount());
removeAccount(account);

expect(store.getState().accounts.items).toEqual([]);
expect(store.getState().accounts.secretKeys).toEqual({});
});

it("calls removeAccountsDependencies with the account", () => {
const {
result: { current: removeAccount },
} = renderHook(() => useRemoveAccount());

removeAccount(mockSocialAccount(5));

expect(mockedRemoveAccountsDependencies).toHaveBeenCalledWith([mockSocialAccount(5)]);
});
});
});
65 changes: 64 additions & 1 deletion src/utils/hooks/setAccountDataHooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { useDispatch } from "react-redux";

import {
useGetAccountsByFingerPrint,
useGetAccountsByType,
useGetNextAvailableAccountLabels,
useImplicitAccounts,
useSeedPhrases,
} from "./getAccountDataHooks";
import { useSelectedNetwork } from "./networkHooks";
import { LedgerAccount, MnemonicAccount, SocialAccount } from "../../types/Account";
import { useRemoveAccountsDependencies } from "./removeAccountDependenciesHooks";
import {
AccountType,
LedgerAccount,
MnemonicAccount,
SecretKeyAccount,
SocialAccount,
} from "../../types/Account";
import { makeDerivationPath } from "../account/derivationPathUtils";
import { makeMnemonicAccount } from "../account/makeMnemonicAccount";
import { decrypt, encrypt } from "../crypto/AES";
Expand All @@ -16,6 +25,8 @@ import { accountsSlice } from "../redux/slices/accountsSlice";
import { restore as restoreFromSecretKey } from "../redux/thunks/secretKeyAccount";
import { derivePublicKeyPair, getFingerPrint } from "../tezos";

const { removeMnemonicAndAccounts, removeNonMnemonicAccounts } = accountsSlice.actions;

const { addAccount } = accountsSlice.actions;

export const useReset = () => () => {
Expand Down Expand Up @@ -173,3 +184,55 @@ export const useRestoreSocial = () => {
dispatch(addAccount(account));
};
};

/**
* Hook for removing all accounts from mnemonic group by a given fingerprint.
*/
export const useRemoveMnemonic = () => {
const dispatch = useAppDispatch();
const getAccountsByFingerPrint = useGetAccountsByFingerPrint();
const removeAccountsDependencies = useRemoveAccountsDependencies();

return (fingerPrint: string) => {
removeAccountsDependencies(getAccountsByFingerPrint(fingerPrint));

dispatch(
removeMnemonicAndAccounts({
fingerPrint,
})
);
};
};

/**
* Hook for removing all accounts of a given type.
*/
export const useRemoveNonMnemonic = () => {
const dispatch = useAppDispatch();
const getAccountsByType = useGetAccountsByType();
const removeAccountsDependencies = useRemoveAccountsDependencies();

return (accountType: AccountType) => {

Check warning on line 215 in src/utils/hooks/setAccountDataHooks.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
removeAccountsDependencies(getAccountsByType(accountType));

Check warning on line 216 in src/utils/hooks/setAccountDataHooks.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

dispatch(
removeNonMnemonicAccounts({
accountType,
})
);

Check warning on line 222 in src/utils/hooks/setAccountDataHooks.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
};
};

/**
* Hook for removing single account.
*/
export const useRemoveAccount = () => {
const dispatch = useAppDispatch();
const removeAccountsDependencies = useRemoveAccountsDependencies();

return (account: SocialAccount | LedgerAccount | SecretKeyAccount) => {
removeAccountsDependencies([account]);

dispatch(accountsSlice.actions.removeAccount(account));
};
};
6 changes: 3 additions & 3 deletions src/utils/redux/slices/accountsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const accountsSlice = createSlice({
},
reducers: {
reset: () => initialState,
// Do not call this directly, use useRemoveMnemonic from removeAccountDataHooks
// Do not call this directly, use useRemoveMnemonic from setAccountDataHooks
removeMnemonicAndAccounts: (
state,
{ payload }: { type: string; payload: { fingerPrint: string } }
Expand All @@ -56,7 +56,7 @@ export const accountsSlice = createSlice({
state.items = newAccounts;
delete state.seedPhrases[fingerPrint];
},
// Do not call this directly, use useRemoveNonMnemonic from removeAccountDataHooks
// Do not call this directly, use useRemoveNonMnemonic from setAccountDataHooks
removeNonMnemonicAccounts: (
state,
{ payload }: { type: string; payload: { accountType: AccountType } }
Expand All @@ -69,7 +69,7 @@ export const accountsSlice = createSlice({
state.secretKeys = {};
}
},
// Do not call this directly, use useRemoveAccount from removeAccountDataHooks
// Do not call this directly, use useRemoveAccount from setAccountDataHooks
removeAccount: (
state,
{ payload }: { type: string; payload: SocialAccount | LedgerAccount | SecretKeyAccount }
Expand Down
7 changes: 5 additions & 2 deletions src/views/home/AccountsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import { NameMultisigFormPage } from "../../components/SendFlow/MultisigAccount/
import colors from "../../style/colors";
import { Account } from "../../types/Account";
import { useAllAccounts, useImplicitAccounts } from "../../utils/hooks/getAccountDataHooks";
import { useRemoveMnemonic, useRemoveNonMnemonic } from "../../utils/hooks/removeAccountDataHooks";
import { useDeriveMnemonicAccount } from "../../utils/hooks/setAccountDataHooks";
import {
useDeriveMnemonicAccount,
useRemoveMnemonic,
useRemoveNonMnemonic,
} from "../../utils/hooks/setAccountDataHooks";
import { useAsyncActionHandler } from "../../utils/hooks/useAsyncActionHandler";
import { useAppSelector } from "../../utils/redux/hooks";

Expand Down

0 comments on commit b13a5dd

Please sign in to comment.