Skip to content

Commit

Permalink
Add tests for account cta
Browse files Browse the repository at this point in the history
  • Loading branch information
OKendigelyan committed Jul 31, 2024
1 parent f156823 commit 283cd23
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { mockImplicitAccount } from "@umami/core";

import { AccountInfoModal } from "./AccountInfoModal";
import { act, render, screen, userEvent, waitFor } from "../../testUtils";

const mockAccount = mockImplicitAccount(0);

describe("<AccountInfoModal />", () => {
it("renders correctly with initial values", async () => {
render(<AccountInfoModal account={mockAccount} />);

await waitFor(() => expect(screen.getByText("Account Info")).toBeVisible());
expect(
screen.getByText(
"You can receive tez or other digital assets by scanning or sharing this QR code"
)
).toBeVisible();
expect(screen.getByText(mockAccount.label)).toBeVisible();
expect(screen.getByText(mockAccount.address.pkh)).toBeVisible();
});

it("displays 'Copied!' message in popover when button is clicked", async () => {
const user = userEvent.setup();

render(<AccountInfoModal account={mockAccount} />);

await act(() => user.click(screen.getByRole("button", { name: "Copy wallet address" })));

await waitFor(() => expect(screen.getByText("Copied!")).toBeVisible());
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { type LinkProps } from "@chakra-ui/react";
import {
type LedgerAccount,
type SecretKeyAccount,
type SocialAccount,
mockImplicitAccount,
mockMnemonicAccount,
mockMultisigAccount,
} from "@umami/core";
import { useSelectedNetwork } from "@umami/state";
import { MAINNET } from "@umami/tezos";

import { AccountInfoModal } from "./AccountInfoModal";
import { AccountSelectorPopover } from "./AccountSelectorPopover";
import { RemoveAccountModal } from "./RemoveAccountModal";
import { RenameAccountPage } from "./RenameAccountModal";
import {
act,
dynamicDisclosureContextMock,
render,
screen,
userEvent,
waitFor,
} from "../../testUtils";

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

const mockAccount = mockImplicitAccount(0, "social");
const mockLinkHandler = jest.fn();

jest.mock("@chakra-ui/react", () => ({
...jest.requireActual("@chakra-ui/react"),
Link: (props: LinkProps) => {
mockLinkHandler(props);
return <div>{props.children}</div>;
},
}));

describe("AccountSelectorPopover", () => {
beforeEach(() => {
jest.mocked(useSelectedNetwork).mockReturnValue(MAINNET);
});

it("renders correctly with initial values", () => {
render(<AccountSelectorPopover account={mockAccount} />);

expect(screen.getByLabelText("Account actions")).toBeVisible();
});

it("displays popover content when trigger is clicked", async () => {
render(<AccountSelectorPopover account={mockAccount} />);
const user = userEvent.setup();

await act(() => user.click(screen.getByLabelText("Account actions")));

await waitFor(() => expect(screen.getByText("Account Info")).toBeVisible());
expect(screen.getByText("Rename")).toBeVisible();
expect(screen.getByText("Remove")).toBeVisible();
expect(screen.getByText("View in TzKT")).toBeVisible();
});

it("opens AccountInfoModal when 'Account Info' button is clicked", async () => {
const { openWith } = dynamicDisclosureContextMock;
const user = userEvent.setup();

render(<AccountSelectorPopover account={mockAccount} />);

await act(async () => {
await user.click(screen.getByLabelText("Account actions"));
await user.click(screen.getByText("Account Info"));
});

expect(openWith).toHaveBeenCalledWith(<AccountInfoModal account={mockAccount} />);
});

it("opens RenameAccountPage when 'Rename' button is clicked", async () => {
const { openWith } = dynamicDisclosureContextMock;
const user = userEvent.setup();

render(<AccountSelectorPopover account={mockAccount} />);

await act(async () => {
await user.click(screen.getByLabelText("Account actions"));
await user.click(screen.getByText("Rename"));
});

expect(openWith).toHaveBeenCalledWith(<RenameAccountPage account={mockAccount} />);
});

it("opens RemoveAccountModal when 'Remove' button is clicked", async () => {
const { openWith } = dynamicDisclosureContextMock;
const user = userEvent.setup();

render(<AccountSelectorPopover account={mockAccount} />);

await act(async () => {
await user.click(screen.getByLabelText("Account actions"));
await user.click(screen.getByText("Remove"));
});

expect(openWith).toHaveBeenCalledWith(
<RemoveAccountModal
account={mockAccount as SocialAccount | LedgerAccount | SecretKeyAccount}
/>
);
});

it("disables 'Remove' button for mnemonic account", async () => {
const mnemonicAccount = mockMnemonicAccount(0);
const user = userEvent.setup();

render(<AccountSelectorPopover account={mnemonicAccount} />);

await act(() => user.click(screen.getByLabelText("Account actions")));
await waitFor(() => expect(screen.queryByText("Remove")).not.toBeInTheDocument());
});

it("disables 'Remove' button for multisig account", async () => {
const multisigAccount = mockMultisigAccount(0);
const user = userEvent.setup();

render(<AccountSelectorPopover account={multisigAccount} />);

await act(() => user.click(screen.getByLabelText("Account actions")));
await waitFor(() => expect(screen.queryByText("Remove")).not.toBeInTheDocument());
});

it("opens correct URL in new tab when 'View in TzKT' button is clicked", async () => {
const user = userEvent.setup();

render(<AccountSelectorPopover account={mockAccount} />);

await act(async () => {
await user.click(screen.getByLabelText("Account actions"));
await user.click(screen.getByText("View in TzKT"));
});

expect(mockLinkHandler).toHaveBeenCalledWith(
expect.objectContaining({ href: `https://tzkt.io/${mockAccount.address.pkh}` })
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Box,
Button,
IconButton,
Link,
Popover,
PopoverBody,
PopoverContent,
Expand Down Expand Up @@ -57,9 +58,9 @@ export const AccountSelectorPopover = ({ account }: AccountSelectorPopoverProps)
<PopoverBody color={color("400")}>
<Box>
<Button
onClick={async e => {
onClick={e => {
e.stopPropagation();
await openWith(<AccountInfoModal account={account} />);
return openWith(<AccountInfoModal account={account} />);
}}
variant="dropdownOption"
>
Expand All @@ -69,9 +70,9 @@ export const AccountSelectorPopover = ({ account }: AccountSelectorPopoverProps)
</Text>
</Button>
<Button
onClick={async e => {
onClick={e => {
e.stopPropagation();
await openWith(<RenameAccountPage account={account} />);
return openWith(<RenameAccountPage account={account} />);
}}
variant="dropdownOption"
>
Expand All @@ -82,9 +83,9 @@ export const AccountSelectorPopover = ({ account }: AccountSelectorPopoverProps)
</Button>
{account.type !== "mnemonic" && account.type !== "multisig" && (
<Button
onClick={async e => {
onClick={e => {
e.stopPropagation();
await openWith(
return openWith(
<RemoveAccountModal
account={account as SocialAccount | LedgerAccount | SecretKeyAccount}
/>
Expand All @@ -98,20 +99,18 @@ export const AccountSelectorPopover = ({ account }: AccountSelectorPopoverProps)
</Text>
</Button>
)}
<Button
onClick={e => {
e.stopPropagation();

const url = `${network.tzktExplorerUrl}/${account.address.pkh}`;
window.open(url, "_blank");
}}
<Link
color={color("400")}
href={`${network.tzktExplorerUrl}/${account.address.pkh}`}
isExternal
onClick={e => e.stopPropagation()}
variant="dropdownOption"
>
<ExternalLinkIcon />
<Text color={color("900")} fontWeight="600">
View in TzKT
</Text>
</Button>
</Link>
</Box>
</PopoverBody>
</PopoverContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { mockSocialAccount } from "@umami/core";
import {
type UmamiStore,
addTestAccount,
addTestAccounts,
makeStore,
useRemoveAccount,
} from "@umami/state";

import { RemoveAccountModal } from "./RemoveAccountModal";
import {
act,
dynamicDisclosureContextMock,
render,
screen,
userEvent,
waitFor,
} from "../../testUtils";

const accounts = [mockSocialAccount(0), mockSocialAccount(1)];

let store: UmamiStore;

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

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

describe("<RemoveAccountModal />", () => {
const mockRemoveAccount = jest.fn();

beforeEach(() => {
jest.mocked(useRemoveAccount).mockReturnValue(mockRemoveAccount);
});

it("renders with default description and button label", async () => {
addTestAccounts(store, accounts);
render(<RemoveAccountModal account={accounts[0]} />, { store });

await waitFor(() => expect(screen.getByText("Remove Account")).toBeVisible());
expect(
screen.getByText(
"Are you sure you want to hide this account? You will need to manually import it again."
)
).toBeVisible();
expect(screen.getByText("Remove")).toBeVisible();
});

it("renders with off-board description and button label when it's the last implicit account", async () => {
addTestAccount(store, accounts[0]);

render(<RemoveAccountModal account={accounts[0]} />, { store });

await waitFor(() => expect(screen.getByText("Remove Account")).toBeVisible());
expect(
screen.getByText(
"Removing your last account will off-board you from Umami. This will remove or reset all customized settings to their defaults. Personal data (including saved contacts, password and accounts) won't be affected."
)
).toBeVisible();
expect(screen.getByText("Remove & Off-board")).toBeVisible();
});

it("handles account removal and navigates correctly when only one account", async () => {
addTestAccount(store, accounts[0]);
const { onClose } = dynamicDisclosureContextMock;
const user = userEvent.setup();

render(<RemoveAccountModal account={accounts[0]} />, { store });

await act(() => user.click(screen.getByText("Remove & Off-board")));

expect(mockRemoveAccount).toHaveBeenCalledWith(accounts[0]);
expect(onClose).toHaveBeenCalled();
});

it("handles account removal and goes back correctly", async () => {
addTestAccounts(store, accounts);

const user = userEvent.setup();
const { goBack } = dynamicDisclosureContextMock;

render(<RemoveAccountModal account={accounts[0]} />, { store });

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

expect(mockRemoveAccount).toHaveBeenCalledWith(accounts[0]);
expect(goBack).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import { useDynamicDisclosureContext } from "@umami/components";
import { type LedgerAccount, type SecretKeyAccount, type SocialAccount } from "@umami/core";
import { useImplicitAccounts, useRemoveAccount } from "@umami/state";
import { useNavigate } from "react-router";

import { AlertIcon } from "../../assets/icons";
import { ModalCloseButton } from "../ModalCloseButton";
Expand All @@ -22,7 +21,6 @@ type RemoveAccountModalProps = {
export const RemoveAccountModal = ({ account }: RemoveAccountModalProps) => {
const { goBack, onClose } = useDynamicDisclosureContext();
const removeAccount = useRemoveAccount();
const navigate = useNavigate();

const isLastImplicitAccount = useImplicitAccounts().length === 1;

Expand All @@ -31,7 +29,7 @@ export const RemoveAccountModal = ({ account }: RemoveAccountModalProps) => {

if (isLastImplicitAccount) {
onClose();
navigate("/");
window.location.replace("/");
} else {
goBack();
}
Expand Down
Loading

0 comments on commit 283cd23

Please sign in to comment.