diff --git a/apps/desktop/src/components/SendFlow/Beacon/TezSignPage.tsx b/apps/desktop/src/components/SendFlow/Beacon/TezSignPage.tsx index feeef8758..0b9780351 100644 --- a/apps/desktop/src/components/SendFlow/Beacon/TezSignPage.tsx +++ b/apps/desktop/src/components/SendFlow/Beacon/TezSignPage.tsx @@ -19,7 +19,7 @@ export const TezSignPage = ({ operation, message }: BeaconSignPageProps) => { return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/ContractCallSignPage.tsx b/apps/web/src/components/SendFlow/sdk/ContractCallSignPage.tsx index 9af917244..a82b38e79 100644 --- a/apps/web/src/components/SendFlow/sdk/ContractCallSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/ContractCallSignPage.tsx @@ -43,7 +43,7 @@ export const ContractCallSignPage = ({ return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/DelegationSignPage.tsx b/apps/web/src/components/SendFlow/sdk/DelegationSignPage.tsx index 8ab37e438..130369b87 100644 --- a/apps/web/src/components/SendFlow/sdk/DelegationSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/DelegationSignPage.tsx @@ -23,7 +23,7 @@ export const DelegationSignPage = ({ return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/FinalizeUnstakeSignPage.tsx b/apps/web/src/components/SendFlow/sdk/FinalizeUnstakeSignPage.tsx index cd1e081e8..70772fb5d 100644 --- a/apps/web/src/components/SendFlow/sdk/FinalizeUnstakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/FinalizeUnstakeSignPage.tsx @@ -23,7 +23,7 @@ export const FinalizeUnstakeSignPage = ({ ); return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/OriginationOperationSignPage.tsx b/apps/web/src/components/SendFlow/sdk/OriginationOperationSignPage.tsx index bed82f9c3..87a978c16 100644 --- a/apps/web/src/components/SendFlow/sdk/OriginationOperationSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/OriginationOperationSignPage.tsx @@ -42,7 +42,7 @@ export const OriginationOperationSignPage = ({ return ( - + diff --git a/apps/web/src/components/SendFlow/sdk/SingleSignPage.test.tsx b/apps/web/src/components/SendFlow/sdk/SingleSignPage.test.tsx new file mode 100644 index 000000000..1f61a0ebe --- /dev/null +++ b/apps/web/src/components/SendFlow/sdk/SingleSignPage.test.tsx @@ -0,0 +1,135 @@ +import { BeaconMessageType, NetworkType, type OperationRequestOutput } from "@airgap/beacon-wallet"; +import type { BatchWalletOperation } from "@taquito/taquito/dist/types/wallet/batch-operation"; +import { + type EstimatedAccountOperations, + type Operation, + executeOperations, + mockContractCall, + mockContractOrigination, + mockDelegationOperation, + mockFinalizeUnstakeOperation, + mockImplicitAccount, + mockStakeOperation, + mockTezOperation, + mockUndelegationOperation, + mockUnstakeOperation, +} from "@umami/core"; +import { WalletClient, makeStore, networksActions, useGetSecretKey } from "@umami/state"; +import { executeParams } from "@umami/test-utils"; +import { GHOSTNET, makeToolkit } from "@umami/tezos"; + +import { + act, + dynamicModalContextMock, + renderInModal, + screen, + userEvent, + waitFor, +} from "../../../testUtils"; +import { SuccessStep } from "../SuccessStep"; +import { type SdkSignPageProps, type SignHeaderProps } from "../utils"; +import { SingleSignPage } from "./SingleSignPage"; + +jest.mock("@umami/core", () => ({ + ...jest.requireActual("@umami/core"), + executeOperations: jest.fn(), + makeToolkit: jest.fn(), +})); + +jest.mock("@umami/tezos", () => ({ + ...jest.requireActual("@umami/tezos"), + makeToolkit: jest.fn(), +})); + +jest.mock("@umami/state", () => ({ + ...jest.requireActual("@umami/state"), + useGetSecretKey: jest.fn(), +})); + +describe("", () => { + it("calls the correct modal", async () => { + const store = makeStore(); + const user = userEvent.setup(); + + const message = { + id: "messageid", + type: BeaconMessageType.OperationRequest, + network: { type: NetworkType.GHOSTNET }, + appMetadata: {}, + } as OperationRequestOutput; + + // check all types of Modals called by SingleSignOperation + const mockedOperations: Record = { + TezSignPage: mockTezOperation(0), + ContractCallSignPage: mockContractCall(0), + DelegationSignPage: mockDelegationOperation(0), + UndelegationSignPage: mockUndelegationOperation(0), + OriginationOperationSignPage: mockContractOrigination(0), + StakeSignPage: mockStakeOperation(0), + UnstakeSignPage: mockUnstakeOperation(0), + FinalizeUnstakeSignPage: mockFinalizeUnstakeOperation(0), + }; + + const operation: EstimatedAccountOperations = { + type: "implicit" as const, + sender: mockImplicitAccount(0), + signer: mockImplicitAccount(0), + operations: [], + estimates: [executeParams({ fee: 123 })], + }; + const headerProps: SignHeaderProps = { + network: GHOSTNET, + appName: message.appMetadata.name, + appIcon: message.appMetadata.icon, + }; + store.dispatch(networksActions.setCurrent(GHOSTNET)); + + for (const key in mockedOperations) { + operation.operations = [mockedOperations[key]]; + const signProps: SdkSignPageProps = { + headerProps: headerProps, + operation: operation, + requestId: { sdkType: "beacon", id: message.id }, + }; + + jest.mocked(useGetSecretKey).mockImplementation(() => () => Promise.resolve("secretKey")); + + jest + .mocked(executeOperations) + .mockResolvedValue({ opHash: "ophash" } as BatchWalletOperation); + jest.spyOn(WalletClient, "respond").mockResolvedValue(); + + await renderInModal(, store); + + expect(screen.getByText("Ghostnet")).toBeInTheDocument(); + expect(screen.queryByText("Mainnet")).not.toBeInTheDocument(); + expect(screen.getByTestId(key)).toBeInTheDocument(); // e.g. TezSignPage + + const signButton = screen.getByRole("button", { + name: "Confirm Transaction", + }); + await waitFor(() => expect(signButton).toBeDisabled()); + + await act(() => user.type(screen.getByLabelText("Password"), "ThisIsAPassword")); + + await waitFor(() => expect(signButton).toBeEnabled()); + await act(() => user.click(signButton)); + + expect(makeToolkit).toHaveBeenCalledWith({ + type: "mnemonic", + secretKey: "secretKey", + network: GHOSTNET, + }); + + await waitFor(() => + expect(WalletClient.respond).toHaveBeenCalledWith({ + type: BeaconMessageType.OperationResponse, + id: message.id, + transactionHash: "ophash", + }) + ); + expect(dynamicModalContextMock.openWith).toHaveBeenCalledWith(); + dynamicModalContextMock.openWith.mockClear(); + } + }); +}); diff --git a/apps/web/src/components/SendFlow/sdk/StakeSignPage.tsx b/apps/web/src/components/SendFlow/sdk/StakeSignPage.tsx index 237dfe4b8..994fe32f5 100644 --- a/apps/web/src/components/SendFlow/sdk/StakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/StakeSignPage.tsx @@ -23,7 +23,7 @@ export const StakeSignPage = ({ return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/UndelegationSignPage.tsx b/apps/web/src/components/SendFlow/sdk/UndelegationSignPage.tsx index c7094ff07..92e0b6ee4 100644 --- a/apps/web/src/components/SendFlow/sdk/UndelegationSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/UndelegationSignPage.tsx @@ -20,7 +20,7 @@ export const UndelegationSignPage = ({ return ( - +
diff --git a/apps/web/src/components/SendFlow/sdk/UnstakeSignPage.tsx b/apps/web/src/components/SendFlow/sdk/UnstakeSignPage.tsx index 3bbb878cf..9f6746a6e 100644 --- a/apps/web/src/components/SendFlow/sdk/UnstakeSignPage.tsx +++ b/apps/web/src/components/SendFlow/sdk/UnstakeSignPage.tsx @@ -23,7 +23,7 @@ export const UnstakeSignPage = ({ return ( - +
diff --git a/packages/core/src/Operation.ts b/packages/core/src/Operation.ts index 709221082..5d88a1819 100644 --- a/packages/core/src/Operation.ts +++ b/packages/core/src/Operation.ts @@ -65,7 +65,7 @@ export type Unstake = { amount: string; }; -type FinalizeUnstake = { +export type FinalizeUnstake = { type: "finalize_unstake"; sender: Address; }; diff --git a/packages/core/src/testUtils.tsx b/packages/core/src/testUtils.tsx index 9088e6af9..807cee911 100644 --- a/packages/core/src/testUtils.tsx +++ b/packages/core/src/testUtils.tsx @@ -40,12 +40,16 @@ import { import { type StoredContactInfo } from "./Contact"; import { type Delegate } from "./Delegate"; import { + type ContractCall, type ContractOrigination, type Delegation, type FA12Transfer, type FA2Transfer, + type FinalizeUnstake, + type Stake, type TezTransfer, type Undelegation, + type Unstake, } from "./Operation"; import { type FA12TokenBalance, @@ -395,6 +399,34 @@ export const mockContractOrigination = ( sender: mockImplicitAddress(index), }); +export const mockContractCall = ( + index: number, + code: MichelsonV1Expression = [] +): ContractCall => ({ + type: "contract_call", + entrypoint: "test-entrypoint", + amount: String(index), + contract: mockContractAddress(index), + args: code, +}); + +export const mockStakeOperation = (index: number): Stake => ({ + type: "stake", + amount: String(index), + sender: mockImplicitAddress(index), +}); + +export const mockUnstakeOperation = (index: number): Unstake => ({ + type: "unstake", + amount: String(index), + sender: mockImplicitAddress(index), +}); + +export const mockFinalizeUnstakeOperation = (index: number): FinalizeUnstake => ({ + type: "finalize_unstake", + sender: mockImplicitAddress(index), +}); + export const mockImplicitContact = (index: number, label?: string): StoredContactInfo => ({ name: label || `Contact ${index}`, pkh: mockImplicitAddress(index).pkh,