@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
218 lines (196 loc) • 7.08 kB
text/typescript
/**
* @jest-environment jsdom
*/
/* eslint-disable @typescript-eslint/consistent-type-assertions */
import { BigNumber } from "bignumber.js";
import { renderHook } from "@testing-library/react";
import { useSendFlowAmountReviewCore } from "../useSendFlowAmountReviewCore";
import type { Transaction, TransactionStatus } from "../../../../generated/types";
import type { AccountLike } from "@ledgerhq/types-live";
const mockCurrency = { ticker: "BTC", type: "CryptoCurrency" as const };
jest.mock("../../../../bridge/impl");
jest.mock("@ledgerhq/ledger-wallet-framework/account/helpers", () => ({
getMainAccount: jest.fn((acc: AccountLike) => acc),
getAccountCurrency: jest.fn(() => mockCurrency),
}));
const createAccount = (): AccountLike =>
({
type: "Account",
id: "bitcoin-account",
name: "Bitcoin",
currency: mockCurrency,
balance: new BigNumber(1000),
spendableBalance: new BigNumber(900),
blockHeight: 800000,
lastSyncDate: new Date(),
}) as unknown as AccountLike;
const createTransaction = (overrides?: Partial<Transaction>): Transaction =>
({
family: "bitcoin",
amount: new BigNumber(100),
recipient: "0xrecipient",
useAllAmount: false,
...overrides,
}) as unknown as Transaction;
const createStatus = (
overrides?: Partial<{ errors: Record<string, Error>; estimatedFees: BigNumber }>,
): TransactionStatus =>
({
errors: overrides?.errors ?? {},
warnings: {},
estimatedFees: overrides?.estimatedFees ?? new BigNumber(10),
amount: new BigNumber(0),
totalSpent: new BigNumber(0),
txInputs: [],
}) as TransactionStatus;
const mockLabels = {
reviewCta: "Review",
getCtaLabel: (currency: string) => `Get ${currency}`,
};
describe("useSendFlowAmountReviewCore", () => {
beforeEach(() => {
jest.clearAllMocks();
const getAccountBridge = jest.requireMock("../../../../bridge/impl").getAccountBridge;
getAccountBridge.mockReturnValue({
updateTransaction: (tx: Transaction, patch: Partial<Transaction>) => ({
...tx,
...patch,
}),
});
});
it("returns mainAccount and accountCurrency", () => {
const account = createAccount();
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction: createTransaction(),
status: createStatus(),
bridgePending: false,
transactionActions: {
updateTransaction: jest.fn((fn: (tx: Transaction) => Transaction) =>
fn(createTransaction()),
),
} as never,
labels: mockLabels,
}),
);
expect(result.current.mainAccount).toBe(account);
expect(result.current.accountCurrency).toBeDefined();
});
it("calls updateTransaction when updateTransactionWithPatch is invoked", () => {
const account = createAccount();
const transaction = createTransaction();
const updateTransaction = jest.fn((fn: (tx: Transaction) => Transaction) => fn(transaction));
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction,
status: createStatus(),
bridgePending: false,
transactionActions: { updateTransaction } as never,
labels: mockLabels,
}),
);
result.current.updateTransactionWithPatch({ amount: new BigNumber(200) });
expect(updateTransaction).toHaveBeenCalledTimes(1);
const updater = updateTransaction.mock.calls[0][0];
const nextTx = updater(transaction);
expect(nextTx).toMatchObject({ amount: new BigNumber(200) });
});
it("computes maxAvailable from spendable minus estimatedFees", () => {
const account = createAccount();
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction: createTransaction(),
status: createStatus({ estimatedFees: new BigNumber(50) }),
bridgePending: false,
transactionActions: { updateTransaction: jest.fn() } as never,
labels: mockLabels,
}),
);
expect(result.current.maxAvailable.toNumber()).toBe(850);
});
it("sets hasInsufficientFundsError when status has NotEnoughBalance amount error", () => {
const account = createAccount();
const status = createStatus();
(status as { errors?: Record<string, Error> }).errors = {
amount: { name: "NotEnoughBalance" } as Error,
};
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction: createTransaction(),
status,
bridgePending: false,
transactionActions: { updateTransaction: jest.fn() } as never,
labels: mockLabels,
}),
);
expect(result.current.hasInsufficientFundsError).toBe(true);
expect(result.current.reviewLabel).toBe("Get BTC");
expect(result.current.reviewShowIcon).toBe(false);
});
it("sets reviewLabel to reviewCta when no insufficient funds error", () => {
const account = createAccount();
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction: createTransaction(),
status: createStatus(),
bridgePending: false,
transactionActions: { updateTransaction: jest.fn() } as never,
labels: mockLabels,
}),
);
expect(result.current.hasInsufficientFundsError).toBe(false);
expect(result.current.reviewLabel).toBe("Review");
expect(result.current.reviewShowIcon).toBe(true);
});
it("sets reviewDisabled when there are errors and no amount", () => {
const account = createAccount();
const status = createStatus();
(status as { errors?: Record<string, Error> }).errors = {
recipient: new Error("Invalid"),
};
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction: createTransaction({ amount: new BigNumber(0), recipient: "" }),
status,
bridgePending: false,
transactionActions: { updateTransaction: jest.fn() } as never,
labels: mockLabels,
}),
);
expect(result.current.hasAmount).toBe(false);
expect(result.current.reviewDisabled).toBe(true);
});
it("sets hasRawAmount and shouldPrepare from transaction", () => {
const account = createAccount();
const transaction = createTransaction({
amount: new BigNumber(50),
recipient: "0xrec",
});
const { result } = renderHook(() =>
useSendFlowAmountReviewCore({
account,
parentAccount: null,
transaction,
status: createStatus(),
bridgePending: true,
transactionActions: { updateTransaction: jest.fn() } as never,
labels: mockLabels,
}),
);
expect(result.current.hasRawAmount).toBe(true);
expect(result.current.shouldPrepare).toBe(true);
expect(result.current.amountComputationPending).toBe(true);
});
});