@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
210 lines (181 loc) • 6.15 kB
text/typescript
import { loadBlacklistedTokenSections } from "./helpers";
import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
jest.mock("@ledgerhq/cryptoassets/state");
const mockGetCryptoAssetsStore = getCryptoAssetsStore as jest.MockedFunction<
typeof getCryptoAssetsStore
>;
const mockEthereumCurrency: CryptoCurrency = {
id: "ethereum",
name: "Ethereum",
ticker: "ETH",
type: "CryptoCurrency",
managerAppName: "Ethereum",
coinType: 60,
scheme: "ethereum",
color: "#0ebdcd",
family: "ethereum",
explorerViews: [],
units: [
{
name: "ether",
code: "ETH",
magnitude: 18,
},
],
};
const mockPolygonCurrency: CryptoCurrency = {
id: "polygon",
name: "Polygon",
ticker: "MATIC",
type: "CryptoCurrency",
managerAppName: "Polygon",
coinType: 60,
scheme: "polygon",
color: "#8247e5",
family: "evm",
explorerViews: [],
units: [
{
name: "matic",
code: "MATIC",
magnitude: 18,
},
],
};
const mockUsdtToken: TokenCurrency = {
id: "ethereum/erc20/usdt",
type: "TokenCurrency",
name: "Tether USD",
ticker: "USDT",
contractAddress: "0xdac17f958d2ee523a2206206994597c13d831ec7",
parentCurrency: mockEthereumCurrency,
tokenType: "erc20",
units: [
{
name: "USDT",
code: "USDT",
magnitude: 6,
},
],
};
const mockUsdcToken: TokenCurrency = {
id: "ethereum/erc20/usdc",
type: "TokenCurrency",
name: "USD Coin",
ticker: "USDC",
contractAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
parentCurrency: mockEthereumCurrency,
tokenType: "erc20",
units: [
{
name: "USDC",
code: "USDC",
magnitude: 6,
},
],
};
const mockMaticUsdtToken: TokenCurrency = {
id: "polygon/erc20/usdt",
type: "TokenCurrency",
name: "Tether USD (Polygon)",
ticker: "USDT",
contractAddress: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
parentCurrency: mockPolygonCurrency,
tokenType: "erc20",
units: [
{
name: "USDT",
code: "USDT",
magnitude: 6,
},
],
};
describe("loadBlacklistedTokenSections", () => {
let mockFindTokenById: jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
mockFindTokenById = jest.fn();
mockGetCryptoAssetsStore.mockReturnValue({
findTokenById: mockFindTokenById,
} as any);
});
it("should return empty array for empty token list", async () => {
const result = await loadBlacklistedTokenSections([]);
expect(result).toEqual([]);
});
it("should create one section for a single token", async () => {
mockFindTokenById.mockResolvedValueOnce(mockUsdtToken);
const result = await loadBlacklistedTokenSections(["ethereum/erc20/usdt"]);
expect(result).toHaveLength(1);
expect(result[0].parentCurrency).toBe(mockEthereumCurrency);
expect(result[0].tokens).toEqual([mockUsdtToken]);
});
it("should group multiple tokens from the same parent currency", async () => {
mockFindTokenById.mockResolvedValueOnce(mockUsdtToken);
mockFindTokenById.mockResolvedValueOnce(mockUsdcToken);
const result = await loadBlacklistedTokenSections([
"ethereum/erc20/usdt",
"ethereum/erc20/usdc",
]);
expect(result).toHaveLength(1);
expect(result[0].parentCurrency).toBe(mockEthereumCurrency);
expect(result[0].tokens).toHaveLength(2);
expect(result[0].tokens).toEqual([mockUsdtToken, mockUsdcToken]);
});
it("should create separate sections for different parent currencies", async () => {
mockFindTokenById.mockResolvedValueOnce(mockUsdtToken);
mockFindTokenById.mockResolvedValueOnce(mockMaticUsdtToken);
const result = await loadBlacklistedTokenSections([
"ethereum/erc20/usdt",
"polygon/erc20/usdt",
]);
expect(result).toHaveLength(2);
expect(result[0].parentCurrency).toBe(mockEthereumCurrency);
expect(result[0].tokens).toEqual([mockUsdtToken]);
expect(result[1].parentCurrency).toBe(mockPolygonCurrency);
expect(result[1].tokens).toEqual([mockMaticUsdtToken]);
});
it("should filter out null/undefined tokens", async () => {
mockFindTokenById.mockResolvedValueOnce(mockUsdtToken);
mockFindTokenById.mockResolvedValueOnce(null);
mockFindTokenById.mockResolvedValueOnce(mockUsdcToken);
const result = await loadBlacklistedTokenSections([
"ethereum/erc20/usdt",
"invalid/token/id",
"ethereum/erc20/usdc",
]);
expect(result).toHaveLength(1);
expect(result[0].parentCurrency).toBe(mockEthereumCurrency);
expect(result[0].tokens).toHaveLength(2);
expect(result[0].tokens).toEqual([mockUsdtToken, mockUsdcToken]);
});
it("should handle complex scenario with mixed parent currencies and null tokens", async () => {
mockFindTokenById.mockResolvedValueOnce(mockUsdtToken);
mockFindTokenById.mockResolvedValueOnce(mockMaticUsdtToken);
mockFindTokenById.mockResolvedValueOnce(null);
mockFindTokenById.mockResolvedValueOnce(mockUsdcToken);
const result = await loadBlacklistedTokenSections([
"ethereum/erc20/usdt",
"polygon/erc20/usdt",
"invalid/token",
"ethereum/erc20/usdc",
]);
expect(result).toHaveLength(2);
expect(result[0].parentCurrency).toBe(mockEthereumCurrency);
expect(result[0].tokens).toHaveLength(2);
expect(result[0].tokens).toEqual([mockUsdtToken, mockUsdcToken]);
expect(result[1].parentCurrency).toBe(mockPolygonCurrency);
expect(result[1].tokens).toHaveLength(1);
expect(result[1].tokens).toEqual([mockMaticUsdtToken]);
});
it("should call findTokenById for all token IDs in parallel", async () => {
mockFindTokenById.mockResolvedValue(mockUsdtToken);
await loadBlacklistedTokenSections(["token1", "token2", "token3"]);
// All calls should be made immediately (Promise.all)
expect(mockFindTokenById).toHaveBeenCalledTimes(3);
expect(mockFindTokenById).toHaveBeenCalledWith("token1");
expect(mockFindTokenById).toHaveBeenCalledWith("token2");
expect(mockFindTokenById).toHaveBeenCalledWith("token3");
});
});