Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
OKendigelyan committed Sep 4, 2024
1 parent c9e5dba commit af66477
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 35 deletions.
2 changes: 1 addition & 1 deletion apps/web/src/components/AccountCard/AccountBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ArrowDownLeftIcon, WalletIcon } from "../../assets/icons";
import { useColor } from "../../styles/useColor";
import { AccountInfoModal } from "../AccountSelectorModal";
import { IconButtonWithText } from "../IconButtonWithText";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";

export const AccountBalance = () => {
const color = useColor();
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/AccountCard/SendTezButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCurrentAccount } from "@umami/state";

import { ArrowUpRightIcon } from "../../assets/icons";
import { IconButtonWithText } from "../IconButtonWithText/IconButtonWithText";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";
import { FormPage as SendTezFormPage } from "../SendFlow/Tez/FormPage";

export const SendTezButton = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useColor } from "../../styles/useColor";
import { AccountTile } from "../AccountTile";
import { ModalCloseButton } from "../CloseButton";
import { OnboardOptionsModal } from "../Onboarding/OnboardOptions";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";

export const AccountSelectorModal = () => {
const accounts = useImplicitAccounts();
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Menu/AdvancedMenu/AdvancedMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useDynamicDrawerContext } from "@umami/components";

import { AlertCircleIcon, LockIcon, RadioIcon } from "../../../assets/icons";
import { useIsAccountVerified } from "../../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../../Onboarding/VerificationFlow";
import { ChangePasswordMenu } from "../ChangePasswordMenu/ChangePasswordMenu";
import { ErrorLogsMenu } from "../ErrorLogsMenu/ErrorLogsMenu";
import { GenericMenu } from "../GenericMenu";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
UserPlusIcon,
} from "../../assets/icons";
import { OnboardOptionsModal } from "../Onboarding/OnboardOptions";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";

export const Menu = () => {
const { openWith: openModal } = useDynamicModalContext();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { mockMnemonicAccount } from "@umami/core";
import { type UmamiStore, addTestAccount, makeStore } from "@umami/state";

import { useHandleVerify } from "./useHandleVerify";
import { VerificationInfoModal } from "./VerificationInfoModal";
import { VerifyMessage } from "./VerifyMessage";
import { dynamicModalContextMock, render, screen, userEvent } from "../../../testUtils";

let store: UmamiStore;

jest.mock("./useHandleVerify.tsx", () => ({
useHandleVerify: jest.fn(),
}));

beforeEach(() => {
store = makeStore();
addTestAccount(store, mockMnemonicAccount(0, { isVerified: false }));
});

describe("<VerifyMessage />", () => {
it("renders correctly", () => {
render(<VerifyMessage />);
Expand All @@ -24,4 +39,16 @@ describe("<VerifyMessage />", () => {

expect(openWith).toHaveBeenCalledWith(<VerificationInfoModal />);
});

it("calls handleVerify when Verify Now is clicked", async () => {
const mockHandleVerify = jest.fn();
jest.mocked(useHandleVerify).mockReturnValue(mockHandleVerify);

const user = userEvent.setup();
render(<VerifyMessage />);

await user.click(screen.getByText("Verify Now"));

expect(mockHandleVerify).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { selectRandomElements } from "@umami/core";
import { type MnemonicAccount, mockMnemonicAccount, selectRandomElements } from "@umami/core";
import { type UmamiStore, addTestAccount, makeStore } from "@umami/state";
import { mnemonic1 } from "@umami/test-utils";

import { VerifySeedphraseModal } from "./VerifySeedphraseModal";
import { act, fireEvent, render, screen, userEvent, waitFor } from "../../../testUtils";
import { act, fireEvent, renderInModal, screen, userEvent, waitFor } from "../../../testUtils";

jest.mock("@umami/core", () => ({
...jest.requireActual("@umami/core"),
Expand All @@ -11,41 +12,46 @@ jest.mock("@umami/core", () => ({

const selectRandomElementsMock = jest.mocked(selectRandomElements);

let store: UmamiStore;

beforeEach(() => {
const splitted = mnemonic1.split(" ").map((value, index) => ({
index,
value,
}));
selectRandomElementsMock.mockReturnValue(splitted.slice(0, 5));
});

const seedPhrase = mnemonic1;
store = makeStore();
addTestAccount(store, mockMnemonicAccount(0, { isVerified: false }));
});

const fixture = () => <VerifySeedphraseModal seedPhrase={seedPhrase} />;
const fixture = () => <VerifySeedphraseModal seedPhrase={mnemonic1} />;

describe("<VerifySeedphraseModal />", () => {
test("when no mnemonic has been entered the button is disabled", () => {
render(fixture());
test("when no mnemonic has been entered the button is disabled", async () => {
await renderInModal(fixture(), store);

expect(screen.getByRole("button", { name: "Continue" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Verify" })).toBeDisabled();
});

test("when an invalid mnemonic has been entered the button is disabled", async () => {
const user = userEvent.setup();

render(fixture());
await renderInModal(fixture(), store);

const inputFields = screen.getAllByRole("textbox");

for (const input of inputFields) {
await act(() => user.type(input, "test"));
}

expect(screen.getByRole("button", { name: "Continue" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Verify" })).toBeDisabled();
});

test("validation is working with all invalid", async () => {
render(fixture());
await renderInModal(fixture(), store);
const inputFields = screen.getAllByRole("textbox");

inputFields.forEach(input => {
fireEvent.change(input, { target: { value: "test" } });
fireEvent.blur(input);
Expand All @@ -57,16 +63,14 @@ describe("<VerifySeedphraseModal />", () => {
});

test("validation is working with some invalid", async () => {
render(fixture());
await renderInModal(fixture(), store);
const inputFields = screen.getAllByRole("textbox");

// Enter correct value
fireEvent.change(inputFields[0], {
target: { value: mnemonic1.split(" ")[0] },
});
fireEvent.blur(inputFields[0]);

// Enter incorrect values
inputFields.forEach(input => {
fireEvent.change(input, { target: { value: "test" } });
fireEvent.blur(input);
Expand All @@ -77,30 +81,25 @@ describe("<VerifySeedphraseModal />", () => {
});

test("validation is working with all valid", async () => {
render(fixture());
await renderInModal(fixture(), store);
const inputFields = screen.getAllByRole("textbox");

// Enter correct value
const splitted = mnemonic1.split(" ");

// Enter incorrect values
inputFields.forEach((input, index) => {
fireEvent.change(input, { target: { value: splitted[index] } });
fireEvent.blur(input);
});

const confirmBtn = screen.getByRole("button", { name: "Continue" });
const confirmBtn = screen.getByRole("button", { name: "Verify" });

await waitFor(() => {
expect(confirmBtn).toBeEnabled();
});

fireEvent.click(confirmBtn);

await waitFor(() => {
expect(goToStepMock).toHaveBeenCalledWith({
type: "nameAccount",
account: { type: "mnemonic", mnemonic: mnemonic1 },
});
expect((store.getState().accounts.items[0] as MnemonicAccount).isVerified).toBe(true);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const VerifySeedphraseModal = ({ seedPhrase }: VerifySeedphraseModalProps
})
);

dispatch(accountsActions.setPassword(""));

onClose();
};

Expand Down Expand Up @@ -115,7 +117,7 @@ export const VerifySeedphraseModal = ({ seedPhrase }: VerifySeedphraseModalProps
</ModalBody>
<ModalFooter flexDirection="column">
<Button width="full" isDisabled={!isValid} type="submit" variant="primary">
Continue
Verify
</Button>

{IS_DEV && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { mockMnemonicAccount } from "@umami/core";
import {
type UmamiStore,
accountsActions,
addTestAccount,
makeStore,
useGetDecryptedMnemonic,
} from "@umami/state";
import { mnemonic1 } from "@umami/test-utils";

import { ImportantNoticeModal } from "./ImportantNoticeModal";
import { useHandleVerify } from "./useHandleVerify";
import { act, dynamicModalContextMock, renderHook } from "../../../testUtils";
import { SetupPassword } from "../SetupPassword";

jest.mock("@umami/state", () => ({
...jest.requireActual("@umami/state"),
useGetDecryptedMnemonic: jest.fn(),
}));

let store: UmamiStore;

beforeEach(() => {
store = makeStore();
addTestAccount(store, mockMnemonicAccount(0, { isVerified: false }));
});

describe("useHandleVerify", () => {
it("should open SetupPassword modal if master password is not set", async () => {
const { openWith } = dynamicModalContextMock;
const { result } = renderHook(() => useHandleVerify(), { store });

await act(async () => await result.current());

expect(openWith).toHaveBeenCalledWith(
<SetupPassword handleProceedToVerification={expect.any(Function)} />
);
});

it("should open ImportantNoticeModal modal if master password is set", async () => {
(useGetDecryptedMnemonic as jest.Mock).mockImplementation(() => () => mnemonic1);
store.dispatch(accountsActions.setPassword("password"));

const { openWith } = dynamicModalContextMock;
const { result } = renderHook(() => useHandleVerify(), { store });

await act(() => result.current());

expect(openWith).toHaveBeenCalledWith(<ImportantNoticeModal seedPhrase={mnemonic1} />, {
size: "xl",
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { mockMnemonicAccount, mockSocialAccount } from "@umami/core";
import { type UmamiStore, addTestAccount, makeStore } from "@umami/state";

import { useIsAccountVerified } from "./useIsAccountVerified";
import { renderHook } from "../../../testUtils";

let store: UmamiStore;

beforeEach(() => {
store = makeStore();
});

describe("useIsAccountVerified", () => {
it("returns true if the account is not mnemonic", () => {
addTestAccount(store, mockSocialAccount(0));
const { result } = renderHook(() => useIsAccountVerified(), { store });

expect(result.current).toBe(true);
});

it("returns true if the account is mnemonic and verified", () => {
addTestAccount(store, mockMnemonicAccount(0));
const { result } = renderHook(() => useIsAccountVerified(), { store });

expect(result.current).toBe(true);
});

it("returns false if the account is mnemonic and not verified", () => {
addTestAccount(store, mockMnemonicAccount(0, { isVerified: false }));
const { result } = renderHook(() => useIsAccountVerified(), { store });

expect(result.current).toBe(false);
});
});
2 changes: 1 addition & 1 deletion apps/web/src/components/ViewOverlay/ViewOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Box, Icon } from "@chakra-ui/react";

import { CoinIcon, LockIcon, PyramidIcon, WalletIcon } from "../../assets/icons";
import { useColor } from "../../styles/useColor";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";

type ViewOverlayProps = {
iconType: "activity" | "earn" | "nfts" | "tokens";
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/views/Activity/Activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { type UIEvent, useRef } from "react";
import loadingDots from "../../assets/loading-dots.gif";
import loadingWheel from "../../assets/loading-wheel.gif";
import { EmptyMessage } from "../../components/EmptyMessage";
import { VerifyMessage } from "../../components/Onboarding/VerificationFlow";
import { useIsAccountVerified } from "../../components/Onboarding/VerificationFlow/useIsAccountVerified";
import { VerifyMessage, useIsAccountVerified } from "../../components/Onboarding/VerificationFlow";
import { OperationTile } from "../../components/OperationTile";
import { ViewOverlay } from "../../components/ViewOverlay/ViewOverlay";
import { useColor } from "../../styles/useColor";
Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/views/NFTs/NFTs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { NFTCard } from "./NFTCard";
import { NFTDrawer } from "./NFTDrawer";
import { NFTFilter, useNFTFilter } from "./NFTFilter";
import { EmptyMessage } from "../../components/EmptyMessage";
import { VerifyMessage } from "../../components/Onboarding/VerificationFlow";
import { useIsAccountVerified } from "../../components/Onboarding/VerificationFlow/useIsAccountVerified";
import { VerifyMessage, useIsAccountVerified } from "../../components/Onboarding/VerificationFlow";
import { ViewOverlay } from "../../components/ViewOverlay/ViewOverlay";
import { useColor } from "../../styles/useColor";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/views/Tokens/Tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCurrentAccount, useGetAccountAllTokens } from "@umami/state";

import { Token } from "./Token";
import { EmptyMessage } from "../../components/EmptyMessage";
import { useIsAccountVerified } from "../../components/Onboarding/VerificationFlow/useIsAccountVerified";
import { useIsAccountVerified } from "../../components/Onboarding/VerificationFlow";
import { VerifyMessage } from "../../components/Onboarding/VerificationFlow/VerifyMessage";
import { ViewOverlay } from "../../components/ViewOverlay/ViewOverlay";

Expand Down
3 changes: 3 additions & 0 deletions packages/state/src/slices/accounts/accounts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe("accountsSlice", () => {
items: [],
seedPhrases: {},
secretKeys: {},
password: "",
});
});

Expand Down Expand Up @@ -55,6 +56,7 @@ describe("accountsSlice", () => {
],
seedPhrases: { mockPrint1: {}, mockPrint2: {} },
secretKeys: {},
password: "",
});

store.dispatch(accountsActions.removeMnemonicAndAccounts({ fingerPrint: "mockPrint1" }));
Expand All @@ -63,6 +65,7 @@ describe("accountsSlice", () => {
items: [mockImplicitAccount(2, undefined, "mockPrint2")],
seedPhrases: { mockPrint2: {} },
secretKeys: {},
password: "",
});
});
});
Expand Down

1 comment on commit af66477

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Title Lines Statements Branches Functions
apps/desktop Coverage: 84%
83.95% (1789/2131) 79.09% (840/1062) 78.57% (451/574)
apps/web Coverage: 84%
83.95% (1789/2131) 79.09% (840/1062) 78.57% (451/574)
packages/components Coverage: 96%
96.89% (125/129) 98.07% (51/52) 84.21% (32/38)
packages/core Coverage: 82%
83.05% (196/236) 73.55% (89/121) 82.14% (46/56)
packages/crypto Coverage: 100%
100% (28/28) 100% (3/3) 100% (5/5)
packages/data-polling Coverage: 98%
96.55% (140/145) 95.45% (21/22) 92.85% (39/42)
packages/multisig Coverage: 98%
98.4% (123/125) 89.47% (17/19) 100% (33/33)
packages/social-auth Coverage: 100%
100% (21/21) 100% (11/11) 100% (3/3)
packages/state Coverage: 83%
83.27% (772/927) 80% (164/205) 78.22% (291/372)
packages/tezos Coverage: 86%
85.57% (89/104) 89.47% (17/19) 82.75% (24/29)
packages/tzkt Coverage: 86%
84.05% (58/69) 81.25% (13/16) 76.92% (30/39)

Please sign in to comment.