Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions explorer_frontend/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
},
"linter": {
"rules": {
"style": {
"noNonNullAssertion": "off"
},
"suspicious": {
"noExplicitAny": "off"
},
Expand Down
2 changes: 2 additions & 0 deletions explorer_frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@
"serve": "^14.2.4",
"solc": "^0.8.29",
"vite-bundle-visualizer": "^1.1.0",
"vite-plugin-circular-dependency": "0.5.0",
"vite-plugin-string": "^1.2.3",
"vitest": "^3.1.1",
"vitest-canvas-mock": "0.3.3",
"wait-on": "^8.0.1"
}
}
2 changes: 1 addition & 1 deletion explorer_frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { RouterProvider } from "atomic-router-react";
import { ErrorBoundary } from "react-error-boundary";
import { useStyletron } from "styletron-react";
import { router } from "./features/routing";
import { RoutesView } from "./features/routing";
import { RoutesView } from "./features/routing/components/RoutesView";
import type { StylesObject } from "./features/shared";

const styles: StylesObject = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
resetTopUpError,
setActiveComponent,
setTopupInput,
topupPanelOpen,
topupSmartAccountTokenFx,
topupTokenEvent,
} from "../model";
Expand Down Expand Up @@ -58,7 +59,7 @@ const TopUpPanel = () => {
]);

useEffect(() => {
setTopupInput({ ...topupInput, amount: "" });
topupPanelOpen();

//Reset error when leaving the page
return () => {
Expand Down
8 changes: 8 additions & 0 deletions explorer_frontend/src/features/account-connector/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
setTopupInput,
topUpEvent,
topUpSmartAccountBalanceFx,
topupPanelOpen,
topupSmartAccountTokenFx,
topupTokenEvent,
} from "./model";
Expand Down Expand Up @@ -383,4 +384,11 @@ $latestActivity.on(addActivity, (_, payload) => {
return payload;
});

$topupInput.on(topupPanelOpen, ({ token }) => {
return {
token,
amount: "",
};
});

$latestActivity.on(clearLatestActivity, () => null);
2 changes: 2 additions & 0 deletions explorer_frontend/src/features/account-connector/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const setTopupInput = createEvent<{
amount: string;
}>();

export const topupPanelOpen = createEvent();

export const topupSmartAccountTokenFx = accountConnectorDomain.createEffect<
{
smartAccount: SmartAccountV1;
Expand Down
121 changes: 71 additions & 50 deletions explorer_frontend/src/features/account/components/AccountInfo.test.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,75 @@
import { renderWithLayout } from "@test/unit/renderWithLayout";
import { screen } from "@testing-library/react";
import { type Scope, type TypeOfSource, fork } from "effector";
import * as effectorReact from "effector-react";
import { describe, it, vi } from "vitest";
import { describe, it } from "vitest";
import { $cometaClient } from "../../cometa/model";
import { measure } from "../../shared/utils/measure";
import {
$account,
$accountCometaInfo,
loadAccountCometaInfoFx,
loadAccountStateFx,
} from "../model";
import { AccountInfo } from "./AccountInfo";

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const getUseUnitReturnedValue = (args: any = {}): any => {
return [
args.account ?? { address: "0x123", balance: "1000" },
args.accountCometaInfo ?? null,
args.isLoading ?? false,
args.isLoadingCometaInfo ?? false,
args.params ?? { address: "0x123" },
args.cometa ?? null,
];
import "../init.ts";
import { addressRoute } from "../../routing/routes/addressRoute.ts";

const initScope = (args?: {
account?: Partial<TypeOfSource<typeof $account>>;
accountCometaInfo?: Partial<TypeOfSource<typeof $accountCometaInfo>>;
isLoading?: boolean;
isLoadingCometaInfo?: boolean;
params?: Partial<TypeOfSource<typeof addressRoute.$params>>;
cometa?: Partial<TypeOfSource<typeof $cometaClient>>;
}): Scope => {
return fork({
values: [
[$account, args?.account ?? { address: "0x123", balance: "1000" }],
[$accountCometaInfo, args?.accountCometaInfo ?? null],
[loadAccountStateFx.pending, args?.isLoading ?? false],
[loadAccountCometaInfoFx.pending, args?.isLoadingCometaInfo ?? false],
[addressRoute.$params, args?.params ?? { address: "0x123" }],
[addressRoute.$isOpened, true],
[$cometaClient, args?.cometa ?? null],
],
});
};

vi.mock("../../routing", () => ({
addressRoute: {
$params: { getState: () => ({ address: "0x123" }) },
$isOpened: { getState: () => true },
},
}));

vi.mock("effector-react");
const mockUseUnit = vi.mocked(effectorReact.useUnit);

describe("AccountInfo", () => {
afterEach(() => {
vi.clearAllMocks();
});

it("renders without crashing", () => {
mockUseUnit.mockReturnValue(getUseUnitReturnedValue());

renderWithLayout(<AccountInfo />);
const scope = initScope();
renderWithLayout(
<effectorReact.Provider value={scope}>
<AccountInfo />
</effectorReact.Provider>,
);

expect(screen.getByTestId("vitest-unit--account-container")).toBeInTheDocument();
});

it("renders skeleton when loading", () => {
mockUseUnit.mockReturnValue(getUseUnitReturnedValue({ account: null, isLoading: true }));

renderWithLayout(<AccountInfo />);
const scope = initScope({ isLoading: true });
renderWithLayout(
<effectorReact.Provider value={scope}>
<AccountInfo />
</effectorReact.Provider>,
);

expect(screen.getByRole("progressbar")).toBeInTheDocument();
});

it("renders account information when loaded", () => {
mockUseUnit.mockReturnValue(
getUseUnitReturnedValue({
account: { address: "0x123", balance: "1000", tokens: [] },
}),
const scope = initScope({
account: { balance: "1000", tokens: [] },
});

renderWithLayout(
<effectorReact.Provider value={scope}>
<AccountInfo />
</effectorReact.Provider>,
);

renderWithLayout(<AccountInfo />);

expect(screen.getByText("Address")).toBeInTheDocument();
expect(screen.getByText("0x123")).toBeInTheDocument();
expect(screen.getByText("Balance")).toBeInTheDocument();
Expand All @@ -67,27 +80,35 @@ describe("AccountInfo", () => {
});

it("renders fallback UI when source code is not available", () => {
mockUseUnit.mockReturnValue(
getUseUnitReturnedValue({
accountCometaInfo: { sourceCode: { Compiled_Contracts: "" } },
}),
const scope = initScope({
accountCometaInfo: {
sourceCode: {
Compiled_Contracts: "",
},
},
});

renderWithLayout(
<effectorReact.Provider value={scope}>
<AccountInfo />
</effectorReact.Provider>,
);

renderWithLayout(<AccountInfo />);

expect(screen.getByText("Source code")).toBeInTheDocument();
expect(screen.getByText("Not available")).toBeInTheDocument();
});

it("shows spinner when cometa info is loading", () => {
mockUseUnit.mockReturnValue(
getUseUnitReturnedValue({
isLoadingCometaInfo: true,
}),
const scope = initScope({
isLoadingCometaInfo: true,
});

renderWithLayout(
<effectorReact.Provider value={scope}>
<AccountInfo />
</effectorReact.Provider>,
);

renderWithLayout(<AccountInfo />);

expect(screen.getByTestId("vitest-unit--loading-cometa-info-spinner")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EditorView } from "@codemirror/view";
import { isHexString } from "@nilfoundation/niljs";
import {
CodeField,
HeadingXLarge,
Expand All @@ -11,8 +12,8 @@ import { useStyletron } from "baseui";
import { useUnit } from "effector-react";
import { useEffect } from "react";
import { $cometaClient } from "../../cometa/model";
import { addressRoute } from "../../routing";
import { Divider } from "../../shared";
import { addressRoute } from "../../routing/routes/addressRoute";
import { Divider } from "../../shared/components/Divider";
import { Info } from "../../shared/components/Info";
import { InfoBlock } from "../../shared/components/InfoBlock";
import { SolidityCodeField } from "../../shared/components/SolidityCodeField";
Expand Down Expand Up @@ -50,7 +51,9 @@ export const AccountInfo = () => {

useEffect(() => {
loadAccountStateFx(params.address);
loadAccountCometaInfoFx({ address: params.address, cometaClient: cometa });
if (cometa && isHexString(params.address)) {
loadAccountCometaInfoFx({ address: params.address, cometaClient: cometa });
}
}, [params.address, cometa]);

if (isLoading) return <AccountLoading />;
Expand All @@ -67,7 +70,7 @@ export const AccountInfo = () => {
<Info
label="Source code"
value={
sourceCode?.length > 0 ? (
sourceCode?.length ? (
<SolidityCodeField
code={sourceCode}
className={css({ marginTop: "1ch" })}
Expand Down
5 changes: 2 additions & 3 deletions explorer_frontend/src/features/account/init.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { sample } from "effector";
import { combine } from "effector";
import { combine, sample } from "effector";
import { $cometaClient } from "../cometa/model";
import { addressRoute } from "../routing";
import { addressRoute } from "../routing/routes/addressRoute";
import { $account, $accountCometaInfo, loadAccountCometaInfoFx, loadAccountStateFx } from "./model";

sample({
Expand Down
5 changes: 2 additions & 3 deletions explorer_frontend/src/features/account/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CometaClient } from "@nilfoundation/niljs";
import type { fetchAccountState } from "../../api/account";

export type AccountState = Awaited<ReturnType<typeof fetchAccountState>>;

export type AccountCometaInfo = {
sourceCode?: string;
};
export type AccountCometaInfo = Awaited<ReturnType<CometaClient["getContract"]>>;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useStyletron } from "baseui";
import { Button } from "baseui/button";
import type { MenuOverrides } from "baseui/menu";
import { type FC, useState } from "react";
import { StatefulPopover } from "../../shared";
import { StatefulPopover } from "../../shared/components/Popover";
import { $availableSolidityVersions, changeSolidityVersion } from "../model";

type CompilerVersionButtonProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { useUnit } from "effector-react";
import { expandProperty } from "inline-style-expand-shorthand";
import type { FC } from "react";
import { playgroundWithHashRoute } from "../../routing";
import { HyperlinkIcon, Link, OverflowEllipsis, StatefulPopover, useMobile } from "../../shared";
import { HyperlinkIcon } from "../../shared/components/HyperlinkIcon";
import { Link } from "../../shared/components/Link";
import { OverflowEllipsis } from "../../shared/components/OverflowEllipsis";
import { StatefulPopover } from "../../shared/components/Popover";
import { useMobile } from "../../shared/hooks/useMobile";
import {
$codeSnippetHash,
$shareCodeSnippetError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import type { MenuOverrides } from "baseui/menu";
import { useUnit } from "effector-react";
import { type FC, useState } from "react";
import { useStyletron } from "styletron-react";
import { StatefulPopover, useMobile } from "../../shared";
import { StatefulPopover } from "../../shared/components/Popover";
import { useMobile } from "../../shared/hooks/useMobile";
import AsyncCallExample from "../assets/AsyncCallExample.sol";
import AsyncRequestExample from "../assets/AsyncRequestExample.sol";
import HandlingExtTxExample from "../assets/HandlingExtTxExample.sol";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const useRepeatedGetTxByHash = (
clearInterval(intervalId);
}
} catch (err) {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
if ((err as any).name === "AbortError") {
return;
}
Expand Down
3 changes: 0 additions & 3 deletions explorer_frontend/src/features/routing/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
export { createRoute } from "./utils/createRoute";
export type { ExtendedRoute } from "./utils/createRoute";
export { RoutesView } from "./components/RoutesView";
export { router } from "./routes/routes";
export * from "./routes/addressRoute";
export * from "./routes/explorerRoute";
Expand Down
1 change: 0 additions & 1 deletion explorer_frontend/src/features/search/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ searchFx.use(async (query) => {
id: block.id,
},
});
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
} catch (e) {
// that's ok
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SPACE } from "@nilfoundation/ui-kit";
import { useStyletron } from "baseui";
import type { ReactNode } from "react";
import type { StyleObject } from "styletron-react";
import { useMobile } from "..";
import { useMobile } from "../hooks/useMobile";

type InfoBlockProps = { children: ReactNode; className?: string };

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { FC, ReactNode } from "react";
import { useStyletron } from "styletron-react";

type InternalPageContainerProps = {
children: ReactNode;
children: JSX.Element | JSX.Element[];
};

export const InternalPageContainer: FC<InternalPageContainerProps> = ({ children }) => {
export const InternalPageContainer = ({ children }: InternalPageContainerProps) => {
const [css] = useStyletron();

return (
Expand Down
Loading
Loading