/* This file is part of GNU Taler (C) 2021 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * * @author Sebastian Javier Marchano (sebasjm) */ import { Amounts, Balance, BalancesResponse, parsePaytoUri } from "@gnu-taler/taler-util"; import { DepositGroupFees } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; import { expect } from "chai"; import { mountHook } from "../test-utils.js"; import { useComponentState } from "./DepositPage.js"; import * as wxApi from "../wxApi.js"; const currency = "EUR" const withoutFee = async (): Promise => ({ coin: Amounts.parseOrThrow(`${currency}:0`), wire: Amounts.parseOrThrow(`${currency}:0`), refresh: Amounts.parseOrThrow(`${currency}:0`) }) const withSomeFee = async (): Promise => ({ coin: Amounts.parseOrThrow(`${currency}:1`), wire: Amounts.parseOrThrow(`${currency}:1`), refresh: Amounts.parseOrThrow(`${currency}:1`) }) const freeJustForIBAN = async (account: string): Promise => /IBAN/i.test(account) ? withoutFee() : withSomeFee() const someBalance = [{ available: 'EUR:10' } as Balance] const nullFunction: any = () => null; type VoidFunction = () => void; describe("DepositPage states", () => { it("should have status 'no-balance' when balance is empty", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:0`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [] }) } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const { status } = getLastResultOrThrow() expect(status).equal("no-balance") } await assertNoPendingUpdate() }); it("should have status 'no-accounts' when balance is not empty and accounts is empty", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:1`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [] }) } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "no-accounts") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; } await assertNoPendingUpdate() }); const ibanPayto = parsePaytoUri("payto://iban/ES8877998399652238")!; const talerBankPayto = parsePaytoUri("payto://x-taler-bank/ES8877998399652238")!; it("should have status 'ready' but unable to deposit ", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:1`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }) } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("0") expect(r.depositHandler.onClick).undefined; } await assertNoPendingUpdate() }); it("should not be able to deposit more than the balance ", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:1`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }), getFeeForDeposit: withoutFee } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("0") expect(r.depositHandler.onClick).undefined; expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) r.amount.onInput("10") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("10") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) expect(r.depositHandler.onClick).undefined; } await assertNoPendingUpdate() }); it("should calculate the fee upon entering amount ", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:1`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }), getFeeForDeposit: withSomeFee } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("0") expect(r.depositHandler.onClick).undefined; expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) r.amount.onInput("10") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("10") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`)) expect(r.depositHandler.onClick).undefined; } await assertNoPendingUpdate() }); it("should calculate the fee upon selecting account ", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:1`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [ibanPayto, talerBankPayto] }), getFeeForDeposit: freeJustForIBAN } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("0") expect(r.depositHandler.onClick).undefined; expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) r.account.onChange("1") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("1") expect(r.amount.value).eq("0") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) expect(r.depositHandler.onClick).undefined; r.amount.onInput("10") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("1") expect(r.amount.value).eq("10") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`)) expect(r.depositHandler.onClick).undefined; r.account.onChange("0") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("10") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:10`)) expect(r.depositHandler.onClick).undefined; } await assertNoPendingUpdate() }); it("should be able to deposit if has the enough balance ", async () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState(currency, nullFunction, nullFunction, { getBalance: async () => ({ balances: [{ available: `${currency}:15`, }] } as Partial), listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }), getFeeForDeposit: withSomeFee } as Partial as any) ); { const { status } = getLastResultOrThrow() expect(status).equal("loading") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("0") expect(r.depositHandler.onClick).undefined; expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) r.amount.onInput("10") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("10") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`)) expect(r.depositHandler.onClick).not.undefined; r.amount.onInput("13") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("13") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:10`)) expect(r.depositHandler.onClick).not.undefined; r.amount.onInput("15") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("15") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:12`)) expect(r.depositHandler.onClick).not.undefined; r.amount.onInput("17") } await waitNextUpdate() { const r = getLastResultOrThrow() if (r.status !== "ready") expect.fail(); expect(r.cancelHandler.onClick).not.undefined; expect(r.currency).eq(currency); expect(r.account.value).eq("0") expect(r.amount.value).eq("17") expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)) expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:14`)) expect(r.depositHandler.onClick).undefined; } await assertNoPendingUpdate() }); });