Skip to content

Commit

Permalink
Fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
OKendigelyan committed Sep 10, 2024
1 parent 743c928 commit ef5238d
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
getAccountGroupLabel,
mockLedgerAccount,
mockMnemonicAccount,
mockSocialAccount,
} from "@umami/core";
import { type UmamiStore, accountsActions, addTestAccounts, makeStore } from "@umami/state";

import { AccountSelectorModal } from "./AccountSelectorModal";
import { DeriveMnemonicAccountModal } from "./DeriveMnemonicAccountModal";
import {
act,
dynamicModalContextMock,
renderInModal,
screen,
userEvent,
waitFor,
} from "../../testUtils";
import { OnboardOptionsModal } from "../Onboarding/OnboardOptions";

let store: UmamiStore;

beforeEach(() => {
store = makeStore();
addTestAccounts(store, [mockMnemonicAccount(0), mockLedgerAccount(1), mockSocialAccount(2)]);
});

describe("<AccountSelectorModal />", () => {
it("renders account groups correctly", async () => {
await renderInModal(<AccountSelectorModal />, store);

await waitFor(() =>
expect(screen.getByText(`Seedphrase ${mockMnemonicAccount(0).seedFingerPrint}`)).toBeVisible()
);
expect(screen.getByText("Social Accounts")).toBeVisible();
expect(screen.getByText("Ledger Accounts")).toBeVisible();
});

it.each([
[
"mnemonic",
mockMnemonicAccount(0),
() => <DeriveMnemonicAccountModal account={mockMnemonicAccount(0)} />,
],
["ledger", mockLedgerAccount(1), () => <OnboardOptionsModal />],
["social", mockSocialAccount(2), () => <OnboardOptionsModal />],
])(
"open appropriate modal when clicking 'Add %s Account' button",
async (_, account, getModalComponent) => {
const user = userEvent.setup();
const accountLabel = getAccountGroupLabel(account);
const { openWith } = dynamicModalContextMock;
await renderInModal(<AccountSelectorModal />, store);

await act(() => user.click(screen.getByLabelText(`Add ${accountLabel} account`)));

expect(openWith).toHaveBeenCalledWith(getModalComponent());
}
);

it("opens appropriate modal when clicking 'Add Account' button", async () => {
const user = userEvent.setup();
const { openWith } = dynamicModalContextMock;
await renderInModal(<AccountSelectorModal />, store);

await act(() => user.click(screen.getByText("Add Account")));

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

it("correctly handles account selection", async () => {
const user = userEvent.setup();
const { onClose } = dynamicModalContextMock;
await renderInModal(<AccountSelectorModal />, store);

const accountTile = screen.getByText(mockMnemonicAccount(0).label);
await act(() => user.click(accountTile));

expect(store.getState().accounts.current).toBe(mockMnemonicAccount(0).address.pkh);
expect(onClose).toHaveBeenCalled();
});

describe("when account is not verified", () => {
beforeEach(() => {
store.dispatch(accountsActions.setCurrent(mockLedgerAccount(0).address.pkh));
store.dispatch(
accountsActions.setIsVerified({
pkh: mockLedgerAccount(0).address.pkh,
isVerified: false,
})
);
});

it("does not render 'Add Account' button", async () => {
await renderInModal(<AccountSelectorModal />, store);

expect(screen.queryByText("Add Account")).not.toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import { useDynamicModalContext } from "@umami/components";
import { type ImplicitAccount, type MnemonicAccount, getAccountGroupLabel } from "@umami/core";
import { accountsActions, useGetAccountBalance, useImplicitAccounts } from "@umami/state";
import { prettyTezAmount } from "@umami/tezos";
import { capitalize } from "lodash";
import { groupBy } from "lodash";
import { useDispatch } from "react-redux";

import { AccountSelectorPopover } from "./AccountSelectorPopover";
import { PlusIcon, TrashIcon } from "../../assets/icons";
import { useColor } from "../../styles/useColor";
import { AccountTile } from "../AccountTile";
import { ModalCloseButton } from "../CloseButton";
import { DeriveMnemonicAccountModal } from "../DeriveMnemonicAccountModal";
import { DeriveMnemonicAccountModal } from "./DeriveMnemonicAccountModal";
import { OnboardOptionsModal } from "../Onboarding/OnboardOptions";
import { useIsAccountVerified } from "../Onboarding/VerificationFlow";

Expand All @@ -37,7 +37,7 @@ export const AccountSelectorModal = () => {

const dispatch = useDispatch();

const groupedAccounts = Object.groupBy(accounts, getAccountGroupLabel);
const groupedAccounts = groupBy(accounts, getAccountGroupLabel);

const handleDeriveAccount = (account?: ImplicitAccount) => {
if (!account) {
Expand All @@ -48,7 +48,7 @@ export const AccountSelectorModal = () => {
case "mnemonic":
return openWith(<DeriveMnemonicAccountModal account={account as MnemonicAccount} />);
default:
break;
return openWith(<OnboardOptionsModal />);
}
};

Expand All @@ -74,7 +74,7 @@ export const AccountSelectorModal = () => {
paddingLeft="12px"
>
<Heading color={color("900")} size="sm">
{type.split("_").map(capitalize).join(" ")}
{type}
</Heading>
<Flex gap="12px">
<IconButton
Expand All @@ -86,15 +86,15 @@ export const AccountSelectorModal = () => {
/>
<IconButton
color={color("500")}
aria-label={`Remove ${type} accounts`}
aria-label={`Add ${type} account`}
icon={<PlusIcon />}
onClick={() => handleDeriveAccount(accounts?.[0])}
onClick={() => handleDeriveAccount(accounts[0])}
size="sm"
variant="ghost"
/>
</Flex>
</Center>
{accounts?.map(account => {
{accounts.map(account => {
const address = account.address.pkh;
const balance = getBalance(address);
const onClick = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { mockMnemonicAccount } from "@umami/core";
import { type UmamiStore, addTestAccount, makeStore, useDeriveMnemonicAccount } from "@umami/state";

import { DeriveMnemonicAccountModal } from "./DeriveMnemonicAccountModal";
import { act, renderInModal, screen, userEvent, waitFor } from "../../../testUtils";

let store: UmamiStore;

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

const mockDeriveMnemonicAccount = jest.fn();

beforeEach(() => {
store = makeStore();
addTestAccount(store, mockMnemonicAccount(0));

jest.mocked(useDeriveMnemonicAccount).mockImplementation(() => mockDeriveMnemonicAccount);
});

describe("<DeriveMnemonicAccountModal />", () => {
it("renders the NameAccountModal with correct subtitle", async () => {
const account = mockMnemonicAccount(0);
await renderInModal(<DeriveMnemonicAccountModal account={account} />, store);

await waitFor(() => {
expect(screen.getByText("Name Your Account")).toBeVisible();
});

expect(
screen.getByText(`Name the new account derived from seedphrase ${account.seedFingerPrint}`)
).toBeVisible();
});

it("handles name submission and opens confirm password modal", async () => {
const user = userEvent.setup();

const account = mockMnemonicAccount(0);
await renderInModal(<DeriveMnemonicAccountModal account={account} />, store);
await act(() => user.type(screen.getByLabelText("Account name"), "Test Account"));
await act(() => user.click(screen.getByRole("button", { name: "Continue" })));

await waitFor(() => {
expect(screen.getByTestId("master-password-modal")).toBeVisible();
});
});

it("derives mnemonic account on password submission", async () => {
const user = userEvent.setup();

const account = mockMnemonicAccount(0);
await renderInModal(<DeriveMnemonicAccountModal account={account} />, store);

await act(() => user.type(screen.getByLabelText("Account name"), "Test Account"));
await act(() => user.click(screen.getByRole("button", { name: "Continue" })));
await act(() => user.type(screen.getByLabelText("Password"), "test-password"));
await act(() => user.click(screen.getByRole("button", { name: "Submit" })));

expect(mockDeriveMnemonicAccount).toHaveBeenCalledWith({
fingerPrint: account.seedFingerPrint,
password: "test-password",
label: "Test Account",
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useDynamicModalContext } from "@umami/components";
import { DEFAULT_ACCOUNT_LABEL, type MnemonicAccount } from "@umami/core";
import { useAsyncActionHandler, useDeriveMnemonicAccount } from "@umami/state";

import { MasterPasswordModal } from "../MasterPasswordModal";
import { NameAccountModal } from "../NameAccountModal";
import { MasterPasswordModal } from "../../MasterPasswordModal";
import { NameAccountModal } from "../../NameAccountModal";

type DeriveMnemonicAccountModalProps = {
account: MnemonicAccount;
Expand All @@ -14,8 +14,8 @@ export const DeriveMnemonicAccountModal = ({ account }: DeriveMnemonicAccountMod
const { goToIndex, openWith } = useDynamicModalContext();

const { handleAsyncAction } = useAsyncActionHandler();
const toast = useToast();
const deriveMnemonicAccount = useDeriveMnemonicAccount();
const toast = useToast();

const handleNameSubmit = ({ accountName }: { accountName: string }) => {
const handlePasswordSubmit = ({ password }: { password: string }) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MasterPasswordModal } from "./MasterPasswordModal";
import { renderInModal, screen, userEvent } from "../../testUtils";

const mockOnSubmit = jest.fn();

describe("<MasterPasswordModal />", () => {
it("calls onSubmit with entered password when form is submitted", async () => {
const user = userEvent.setup();
await renderInModal(<MasterPasswordModal onSubmit={mockOnSubmit} />);

await user.type(screen.getByLabelText("Password"), "testpassword");
await user.click(screen.getByRole("button", { name: "Submit" }));

expect(mockOnSubmit).toHaveBeenCalledWith({ password: "testpassword" });
});

it("shows validation error when submitting without a password", async () => {
const user = userEvent.setup();
await renderInModal(<MasterPasswordModal onSubmit={mockOnSubmit} />);

await user.click(screen.getByRole("button", { name: "Submit" }));

expect(screen.getByText("Password is required")).toBeInTheDocument();
expect(mockOnSubmit).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const MasterPasswordModal = ({ onSubmit }: MasterPasswordModalProps) => {
});

return (
<ModalContent>
<ModalContent data-testid="master-password-modal">
<ModalHeader>
<ModalBackButton />
<ModalCloseButton />
Expand Down
28 changes: 28 additions & 0 deletions apps/web/src/components/NameAccountModal/NameAccountModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NameAccountModal } from "./NameAccountModal";
import { act, renderInModal, screen, userEvent, waitFor } from "../../testUtils";

const mockOnSubmit = jest.fn();

describe("NameAccountModal", () => {
it("renders with custom title and subtitle", async () => {
await renderInModal(
<NameAccountModal onSubmit={mockOnSubmit} subtitle="Custom Subtitle" title="Custom Title" />
);

await waitFor(() => {
expect(screen.getByText("Custom Title")).toBeVisible();
});
expect(screen.getByText("Custom Subtitle")).toBeVisible();
});

it("calls onSubmit with form data when submitted", async () => {
const user = userEvent.setup();

await renderInModal(<NameAccountModal onSubmit={mockOnSubmit} />);

await act(() => user.type(screen.getByLabelText("Account name"), "Test Account"));
await act(() => user.click(screen.getByRole("button", { name: "Continue" })));

expect(mockOnSubmit).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const NameAccountModal = ({
const { register, handleSubmit } = form;

return (
<ModalContent>
<ModalContent data-testid="name-account-modal">
<ModalHeader>
<ModalBackButton />
<ModalCloseButton />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ describe("<SetupPassword />", () => {

describe("mnemonic mode", () => {
let store: UmamiStore;
const allFormValues = { mnemonic: mnemonic1.split(" ").map(word => ({ val: word })) };
const allFormValues = {
current: { mnemonic: mnemonic1.split(" ").map(word => ({ val: word })) },
};
const mockRestoreFromMnemonic = jest.fn();

beforeEach(() => {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/testUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ export const renderInModal = async (

if (allFormValues) {
await act(() =>
result.current.openWith(<DummyForm defaultValues={allFormValues} nextPage={component} />)
result.current.openWith(
<DummyForm defaultValues={allFormValues.current ?? allFormValues} nextPage={component} />
)
);
fireEvent.click(screen.getByRole("button"));
}
Expand Down

1 comment on commit ef5238d

@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: 83%
83.79% (1758/2098) 78.94% (825/1045) 78.45% (448/571)
apps/web Coverage: 83%
83.79% (1758/2098) 78.94% (825/1045) 78.45% (448/571)
packages/components Coverage: 97%
97.1% (134/138) 96.49% (55/57) 82.92% (34/41)
packages/core Coverage: 82%
82.89% (223/269) 73.18% (101/138) 81.35% (48/59)
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: 84%
83.64% (772/923) 80.78% (164/203) 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.