Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement network switch #956

Merged
merged 68 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
da1bc5a
wip
goga-m Feb 6, 2025
6b0d7dc
wip
goga-m Feb 6, 2025
f23cc88
Merge branch 'feat/evm' into feat/network-switch
goga-m Feb 6, 2025
a47ad3e
wip
goga-m Feb 6, 2025
cb4abe2
wip
goga-m Feb 6, 2025
b91af2b
style: resolve style guide violations
goga-m Feb 6, 2025
5c607c3
wip
goga-m Feb 6, 2025
fc1e669
Merge branch 'feat/evm' into feat/network-switch
goga-m Feb 6, 2025
64d9add
wip
goga-m Feb 7, 2025
7d9ced8
Merge branch 'feat/network-switch' of github.com:ArdentHQ/arkvault in…
goga-m Feb 7, 2025
33a5912
style: resolve style guide violations
goga-m Feb 7, 2025
e61ba30
wip
goga-m Feb 7, 2025
4134bc6
Merge branch 'feat/network-switch' of github.com:ArdentHQ/arkvault in…
goga-m Feb 7, 2025
c3742d5
wip
goga-m Feb 7, 2025
c1226e3
wip
goga-m Feb 7, 2025
541ec68
wip
goga-m Feb 7, 2025
b1ab568
wip
goga-m Feb 7, 2025
7553d32
wip
goga-m Feb 7, 2025
efa8b1f
wip
goga-m Feb 7, 2025
810b9ed
wip
goga-m Feb 7, 2025
bab7c69
wip
goga-m Feb 7, 2025
2754ff9
wip
goga-m Feb 7, 2025
7cd444d
wip
goga-m Feb 7, 2025
9ea002a
wip
goga-m Feb 10, 2025
ee88cce
style: resolve style guide violations
goga-m Feb 10, 2025
804e9f7
Merge branch 'feat/evm' into feat/network-switch
goga-m Feb 10, 2025
d57809d
wip
goga-m Feb 10, 2025
83d1cee
wip
goga-m Feb 10, 2025
c14ed85
wip
goga-m Feb 10, 2025
4c4921e
wip
goga-m Feb 10, 2025
017c840
wip
goga-m Feb 10, 2025
de9c19a
wip
goga-m Feb 10, 2025
0195546
wip
goga-m Feb 10, 2025
e112df6
Merge branch 'feat/evm' into feat/network-switch
goga-m Feb 10, 2025
57bd36f
wip
goga-m Feb 10, 2025
cda8eb7
wip
goga-m Feb 10, 2025
b521596
wip
goga-m Feb 10, 2025
9d73553
wip
goga-m Feb 10, 2025
e3b1a85
wip
goga-m Feb 10, 2025
85fb48a
wip
goga-m Feb 10, 2025
60c8c42
style: resolve style guide violations
goga-m Feb 10, 2025
3f8e733
wip
goga-m Feb 11, 2025
2da4b38
wip
goga-m Feb 11, 2025
4e7a501
wip
goga-m Feb 11, 2025
58f6d49
wip
goga-m Feb 11, 2025
92ebeb5
style: resolve style guide violations
goga-m Feb 11, 2025
1fbbee9
wip
goga-m Feb 11, 2025
d244ceb
Merge branch 'feat/network-switch' of github.com:ArdentHQ/arkvault in…
goga-m Feb 11, 2025
4c0d253
wip
goga-m Feb 11, 2025
d615268
wip
goga-m Feb 11, 2025
99c37d4
style: resolve style guide violations
goga-m Feb 11, 2025
69dbf95
wip
goga-m Feb 11, 2025
3271077
wip
goga-m Feb 11, 2025
494d5ea
wip
goga-m Feb 11, 2025
0990b10
wip
goga-m Feb 11, 2025
23c0a0f
wip
goga-m Feb 11, 2025
da5f5ac
wip
goga-m Feb 11, 2025
984c2bf
wip
goga-m Feb 11, 2025
256e0b1
style: resolve style guide violations
goga-m Feb 11, 2025
31abd9e
wip
goga-m Feb 11, 2025
d7a7caa
wip
goga-m Feb 11, 2025
86b7107
wip
goga-m Feb 11, 2025
4642c72
wip
goga-m Feb 11, 2025
829fffa
wip
goga-m Feb 11, 2025
0553771
wip
goga-m Feb 11, 2025
56ed73f
Merge branch 'feat/network-switch' of github.com:ArdentHQ/arkvault in…
goga-m Feb 11, 2025
6742584
wip
goga-m Feb 11, 2025
f521c1f
Merge branch 'feat/evm' into feat/network-switch
goga-m Feb 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ jobs:
COVERAGE_THRESHOLD_LINES: 99.57
COVERAGE_THRESHOLD_FUNCTIONS: 98.3
COVERAGE_THRESHOLD_STATEMENTS: 99.6
COVERAGE_THRESHOLD_BRANCHES: 99.2
COVERAGE_THRESHOLD_BRANCHES: 99.17
COVERAGE_INCLUDE_PATH: src/domains/message
with:
timeout_minutes: 10
Expand Down Expand Up @@ -612,7 +612,7 @@ jobs:
run: pnpm rebuild
- name: Test
env:
COVERAGE_THRESHOLD_BRANCHES: 99.6
COVERAGE_THRESHOLD_BRANCHES: 99.24
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
Expand Down Expand Up @@ -855,9 +855,9 @@ jobs:
- name: Test
uses: nick-invision/retry@v2
env:
COVERAGE_THRESHOLD_LINES: 96.44
COVERAGE_THRESHOLD_FUNCTIONS: 97.51
COVERAGE_THRESHOLD_STATEMENTS: 96.5
COVERAGE_THRESHOLD_LINES: 96.13
COVERAGE_THRESHOLD_FUNCTIONS: 97.34
COVERAGE_THRESHOLD_STATEMENTS: 96.21
COVERAGE_THRESHOLD_BRANCHES: 92.27
COVERAGE_INCLUDE_PATH: src/domains/wallet
with:
Expand Down
70 changes: 70 additions & 0 deletions src/app/App.restore.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Bcrypt } from "@ardenthq/sdk-cryptography";
import { createHashHistory } from "history";
import React from "react";
import userEvent from "@testing-library/user-event";
import { App } from "./App";
import { toasts } from "@/app/services";
import { translations as profileTranslations } from "@/domains/profile/i18n";
import { env, render, screen, waitFor } from "@/utils/testing-library";

const history = createHashHistory();

const passwordInput = () => screen.getByTestId("SignIn__input--password");

describe("App", () => {
afterEach(() => {
vi.restoreAllMocks();
process.env.MOCK_SYNCHRONIZER = undefined;
});

beforeEach(() => {
vi.spyOn(toasts, "dismiss").mockImplementation(vi.fn());

// Mock synchronizer to avoid running any jobs in these tests.
process.env.MOCK_SYNCHRONIZER = "TRUE";

history.replace("/");
env.reset();
});

it("should redirect to root if profile restoration error occurs", async () => {
process.env.TEST_PROFILES_RESTORE_STATUS = "restored";
process.env.REACT_APP_IS_UNIT = "1";

render(<App />, { history, withProviders: false });

await expect(
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined),
).resolves.toBeVisible();

expect(history.location.pathname).toBe("/");

await userEvent.click(screen.getAllByTestId("ProfileRow__Link")[1]);

await waitFor(() => {
expect(passwordInput()).toBeInTheDocument();
});

await userEvent.clear(passwordInput());
await userEvent.type(passwordInput(), "password");

await waitFor(() => {
expect(passwordInput()).toHaveValue("password");
});

const profile = env.profiles().findById("cba050f1-880f-45f0-9af9-cfe48f406052");

const verifyPasswordMock = vi.spyOn(Bcrypt, "verify").mockReturnValue(true);
const memoryPasswordMock = vi.spyOn(profile.password(), "get").mockImplementation(() => {
throw new Error("password not found");
});

await userEvent.click(screen.getByTestId("SignIn__submit-button"));

await waitFor(() => expect(memoryPasswordMock).toHaveBeenCalled(), { timeout: 4000 });
await waitFor(() => expect(history.location.pathname).toBe("/"));

memoryPasswordMock.mockRestore();
verifyPasswordMock.mockRestore();
});
});
85 changes: 22 additions & 63 deletions src/app/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/require-await */
import { Bcrypt } from "@ardenthq/sdk-cryptography";
import { Contracts, Environment } from "@ardenthq/sdk-profiles";
import { createHashHistory } from "history";
import React from "react";
Expand Down Expand Up @@ -73,46 +72,6 @@ describe("App", () => {
env.reset();
});

it("should redirect to root if profile restoration error occurs", async () => {
process.env.REACT_APP_IS_UNIT = "1";

render(<App />, { history, withProviders: false });

await expect(
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined),
).resolves.toBeVisible();

expect(history.location.pathname).toBe("/");

await userEvent.click(screen.getAllByTestId("ProfileRow__Link")[1]);

await waitFor(() => {
expect(passwordInput()).toBeInTheDocument();
});

await userEvent.clear(passwordInput());
await userEvent.type(passwordInput(), "password");

await waitFor(() => {
expect(passwordInput()).toHaveValue("password");
});

const profile = env.profiles().findById("cba050f1-880f-45f0-9af9-cfe48f406052");

const verifyPasswordMock = vi.spyOn(Bcrypt, "verify").mockReturnValue(true);
const memoryPasswordMock = vi.spyOn(profile.password(), "get").mockImplementation(() => {
throw new Error("password not found");
});

await userEvent.click(screen.getByTestId("SignIn__submit-button"));

await waitFor(() => expect(memoryPasswordMock).toHaveBeenCalled(), { timeout: 4000 });
await waitFor(() => expect(history.location.pathname).toBe("/"));

memoryPasswordMock.mockRestore();
verifyPasswordMock.mockRestore();
});

it("should render page skeleton", async () => {
const toastSuccessMock = vi.spyOn(toasts, "success").mockImplementation(vi.fn());
process.env.REACT_APP_IS_UNIT = "1";
Expand Down Expand Up @@ -257,15 +216,25 @@ describe("App", () => {
},
);

it("should enter profile", async () => {
it("should enter profile and fail to restore", async () => {
process.env.REACT_APP_IS_UNIT = "1";
process.env.TEST_PROFILES_RESTORE_STATUS = undefined;

render(<App />, { history, withProviders: false });

await expect(
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined),
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined, { timeout: 2000 }),
).resolves.toBeVisible();

const profile = env.profiles().findById("cba050f1-880f-45f0-9af9-cfe48f406052");

vi.spyOn(profile, "usesPassword").mockReturnValue(true);
vi.spyOn(profile.password(), "get").mockImplementation(() => {
throw new Error("Failed to restore");
});

await env.profiles().restore(passwordProtectedProfile, getDefaultPassword());

expect(history.location.pathname).toBe("/");

await userEvent.click(screen.getAllByTestId("ProfileRow__Link")[1]);
Expand All @@ -282,33 +251,27 @@ describe("App", () => {

const toastSpy = vi.spyOn(toasts, "dismiss").mockResolvedValue(undefined);

vi.spyOn(passwordProtectedProfile.password(), "get").mockImplementation(() => {
throw new Error("restore error");
});

await userEvent.click(screen.getByTestId("SignIn__submit-button"));

const profileDashboardUrl = `/profiles/${passwordProtectedProfile.id()}/dashboard`;
await waitFor(() => expect(history.location.pathname).toBe(profileDashboardUrl), { timeout: 4000 });
await waitFor(() => expect(history.location.pathname).toBe("/"), { timeout: 4000 });

toastSpy.mockRestore();
vi.restoreAllMocks();
});

it("should enter profile and fail to restore", async () => {
it("should enter profile", async () => {
process.env.REACT_APP_IS_UNIT = "1";
process.env.TEST_PROFILES_RESTORE_STATUS = undefined;

render(<App />, { history, withProviders: false });

await expect(
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined, { timeout: 2000 }),
screen.findByText(profileTranslations.PAGE_WELCOME.WITH_PROFILES.TITLE, undefined),
).resolves.toBeVisible();

const profile = env.profiles().findById("cba050f1-880f-45f0-9af9-cfe48f406052");

vi.spyOn(profile, "usesPassword").mockReturnValue(true);
vi.spyOn(profile.password(), "get").mockImplementation(() => {
throw new Error("Failed to restore");
});

await env.profiles().restore(passwordProtectedProfile, getDefaultPassword());

expect(history.location.pathname).toBe("/");

await userEvent.click(screen.getAllByTestId("ProfileRow__Link")[1]);
Expand All @@ -325,15 +288,11 @@ describe("App", () => {

const toastSpy = vi.spyOn(toasts, "dismiss").mockResolvedValue(undefined);

vi.spyOn(passwordProtectedProfile.password(), "get").mockImplementation(() => {
throw new Error("restore error");
});

await userEvent.click(screen.getByTestId("SignIn__submit-button"));

await waitFor(() => expect(history.location.pathname).toBe("/"), { timeout: 4000 });
const profileDashboardUrl = `/profiles/${passwordProtectedProfile.id()}/dashboard`;
await waitFor(() => expect(history.location.pathname).toBe(profileDashboardUrl), { timeout: 4000 });

toastSpy.mockRestore();
vi.restoreAllMocks();
});
});
4 changes: 4 additions & 0 deletions src/app/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import List from "./list.svg?react";
import LoaderLogo from "./loader-logo.svg?react";
import Lock from "./lock.svg?react";
import MagnifyingGlass from "./magnifying-glass.svg?react";
import Mainnet from "./mainnet.svg?react";
import Testnet from "./testnet.svg?react";
import MagnifyingGlassAlt from "./magnifying-glass-alt.svg?react";
import MagnifyingGlassId from "./magnifying-glass-id.svg?react";
import Menu from "./menu.svg?react";
Expand Down Expand Up @@ -178,6 +180,7 @@ export const SvgCollection: Record<string, FC<SVGProps<SVGSVGElement>>> = {
MagnifyingGlass,
MagnifyingGlassAlt,
MagnifyingGlassId,
Mainnet,
Menu,
MenuOpen,
MoneyCoinSwap,
Expand All @@ -202,6 +205,7 @@ export const SvgCollection: Record<string, FC<SVGProps<SVGSVGElement>>> = {
StatusError,
StatusOk,
StatusStandby,
Testnet,
Trash,
UnderlineMoon,
UnderlineSun,
Expand Down
5 changes: 5 additions & 0 deletions src/app/assets/svg/mainnet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/app/assets/svg/testnet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/app/components/Dropdown/Dropdown.contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface DropdownProperties extends JSX.IntrinsicAttributes {
as?: React.ElementType;
children?: React.ReactElement;
top?: React.ReactNode;
bottom?: React.ReactNode;
onSelect?: OnSelectProperties;
variant?: DropdownVariantType;
options?: DropdownOption[] | DropdownOptionGroup[];
Expand Down
4 changes: 3 additions & 1 deletion src/app/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const Wrapper = ({ variant, ...props }: { variant: DropdownVariantType }
export const Dropdown: FC<DropdownProperties> = ({
children,
top,
bottom,
wrapperClass,
variant,
options,
Expand Down Expand Up @@ -127,11 +128,12 @@ export const Dropdown: FC<DropdownProperties> = ({
>
<Wrapper
variant={variant || options ? "options" : "custom"}
className="dropdown-body overflow-hidden rounded-xl bg-theme-background px-1 py-0 shadow-xl"
className="dropdown-body overflow-hidden rounded-xl bg-theme-background py-0 shadow-xl md:px-1"
>
{top}
{options?.length && renderOptions({ onSelect: onSelectOption, options })}
{clonedElement && <div>{clonedElement}</div>}
{bottom}
</Wrapper>
</div>
</FloatingPortal>
Expand Down
Loading
Loading