diff --git a/frontend/src/lib/services/accounts-balances.services.ts b/frontend/src/lib/services/accounts-balances.services.ts new file mode 100644 index 00000000000..35a4af61d68 --- /dev/null +++ b/frontend/src/lib/services/accounts-balances.services.ts @@ -0,0 +1,41 @@ +import { uncertifiedLoadSnsesAccountsBalances } from "$lib/services/sns-accounts-balance.services"; +import { uncertifiedLoadAccountsBalance } from "$lib/services/wallet-uncertified-accounts.services"; +import type { UniverseCanisterIdText } from "$lib/types/universe"; +import type { CanisterIdString } from "@dfinity/nns"; +import { Principal } from "@dfinity/principal"; + +const loadedBalances = new Set(); +export const resetBalanceLoading = (): void => { + loadedBalances.clear(); +}; + +const getNotLoadedIds = (ids: CanisterIdString[]): CanisterIdString[] => { + const notLoadedIds = ids.filter((id) => !loadedBalances.has(id)); + notLoadedIds.forEach((id) => loadedBalances.add(id)); + return notLoadedIds; +}; + +export const loadSnsAccountsBalances = async ( + rootCanisterIds: Principal[] +): Promise => { + const stringIds = rootCanisterIds.map((id) => id.toText()); + const notLoadedIds = getNotLoadedIds(stringIds); + + if (notLoadedIds.length === 0) return; + + await uncertifiedLoadSnsesAccountsBalances({ + rootCanisterIds: notLoadedIds.map((id) => Principal.fromText(id)), + }); +}; + +export const loadAccountsBalances = async ( + universeIds: UniverseCanisterIdText[] +): Promise => { + const notLoadedIds = getNotLoadedIds(universeIds); + + if (notLoadedIds.length === 0) return; + + await uncertifiedLoadAccountsBalance({ + universeIds: notLoadedIds, + }); +}; diff --git a/frontend/src/tests/lib/services/accounts-balances.services.spec.ts b/frontend/src/tests/lib/services/accounts-balances.services.spec.ts new file mode 100644 index 00000000000..fb279801375 --- /dev/null +++ b/frontend/src/tests/lib/services/accounts-balances.services.spec.ts @@ -0,0 +1,109 @@ +import { + loadAccountsBalances, + loadSnsAccountsBalances, + resetBalanceLoading, +} from "$lib/services/accounts-balances.services"; +import * as snsBalanceServices from "$lib/services/sns-accounts-balance.services"; +import * as walletServices from "$lib/services/wallet-uncertified-accounts.services"; +import type { CanisterIdString } from "@dfinity/nns"; +import { Principal } from "@dfinity/principal"; + +vi.mock("$lib/services/icrc-accounts.services", () => { + return { + loadAccounts: vi.fn(), + loadIcrcToken: vi.fn(), + }; +}); + +vi.mock("$lib/services/sns-accounts.services", () => { + return { + loadSnsAccounts: vi.fn(), + }; +}); + +describe("accounts-balances services", () => { + let accountsBalanceSpy; + let snsBalancesSpy; + + beforeEach(() => { + resetBalanceLoading(); + + accountsBalanceSpy = vi.spyOn( + walletServices, + "uncertifiedLoadAccountsBalance" + ); + + snsBalancesSpy = vi.spyOn( + snsBalanceServices, + "uncertifiedLoadSnsesAccountsBalances" + ); + }); + + describe("loadSnsBalances", () => { + it("should not call service if array is empty", async () => { + await loadSnsAccountsBalances([]); + expect(snsBalancesSpy).not.toHaveBeenCalled(); + }); + + it("should call service with correct parameters", async () => { + const principal1 = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); + const principal2 = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); + + await loadSnsAccountsBalances([principal1, principal2]); + + expect(snsBalancesSpy).toHaveBeenCalledWith({ + rootCanisterIds: [principal1, principal2], + }); + }); + + it("should not reload already loaded canister IDs", async () => { + const principal1 = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); + + await loadSnsAccountsBalances([principal1]); + await loadSnsAccountsBalances([principal1]); + + expect(snsBalancesSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe("loadAccountsBalances", () => { + it("should not call service if array is empty", async () => { + await loadAccountsBalances([]); + expect(accountsBalanceSpy).not.toHaveBeenCalled(); + }); + + it("should call service with correct parameters", async () => { + const universeIds: CanisterIdString[] = [ + "rrkah-fqaaa-aaaaa-aaaaq-cai", + "ryjl3-tyaaa-aaaaa-aaaba-cai", + ]; + + await loadAccountsBalances(universeIds); + + expect(accountsBalanceSpy).toHaveBeenCalledWith({ + universeIds, + }); + }); + + it("should not reload already loaded universe IDs", async () => { + const universeIds: CanisterIdString[] = ["rrkah-fqaaa-aaaaa-aaaaq-cai"]; + + await loadAccountsBalances(universeIds); + await loadAccountsBalances(universeIds); + + expect(accountsBalanceSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe("resetBalanceLoading", () => { + it("should allow reloading previously loaded IDs after reset", async () => { + const universeIds: CanisterIdString[] = ["rrkah-fqaaa-aaaaa-aaaaq-cai"]; + + await loadAccountsBalances(universeIds); + resetBalanceLoading(); + await loadAccountsBalances(universeIds); + + expect(accountsBalanceSpy).toHaveBeenCalledTimes(2); + }); + }); +});