/* This file is part of GNU Taler (C) 2022 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 { AmountString, Amounts, ExchangeEntryStatus, ExchangeListItem, ExchangeTosStatus, ScopeType, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import * as tests from "@gnu-taler/web-util/testing"; import { createWalletApiMock } from "../../test-utils.js"; import { useComponentStateFromURI } from "./state.js"; const exchanges: ExchangeListItem[] = [ { currency: "ARS", exchangeBaseUrl: "http://exchange.demo.taler.net", paytoUris: [], tosStatus: ExchangeTosStatus.Accepted, exchangeStatus: ExchangeEntryStatus.Used, permanent: true, auditors: [ { auditor_pub: "pubpubpubpubpub", auditor_url: "https://audotor.taler.net", denomination_keys: [], }, ], denomFees: { deposit: [], refresh: [], refund: [], withdraw: [], }, globalFees: [], transferFees: {}, wireInfo: { accounts: [], feesForType: {}, }, } as Partial as ExchangeListItem, ]; const nullFunction = async (): Promise => { null; }; describe("Withdraw CTA states", () => { it("should tell the user that the URI is missing", async () => { const { handler, TestingContext } = createWalletApiMock(); const props = { talerWithdrawUri: undefined, cancel: nullFunction, onSuccess: nullFunction, }; const hookBehavior = await tests.hookBehaveLikeThis( useComponentStateFromURI, props, [ ({ status }) => { expect(status).equals("loading"); }, ({ status, error }) => { if (status != "error") expect.fail(); if (!error) expect.fail(); // if (!error.hasError) expect.fail(); // if (error.operational) expect.fail(); expect(error.description).eq("ERROR_NO-URI-FOR-WITHDRAWAL"); }, ], TestingContext, ); expect(hookBehavior).deep.equal({ result: "ok" }); expect(handler.getCallingQueueState()).eq("empty"); }); it("should tell the user that there is not known exchange", async () => { const { handler, TestingContext } = createWalletApiMock(); const props = { talerWithdrawUri: "taler-withdraw://", cancel: nullFunction, onSuccess: nullFunction, }; handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForUri, undefined, { status: "pending", operationId: "123", amount: "EUR:2" as AmountString, possibleExchanges: [], }, ); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalTransactionByUri, undefined, { transactionId: "123" } as any, ); const hookBehavior = await tests.hookBehaveLikeThis( useComponentStateFromURI, props, [ ({ status }) => { expect(status).equals("loading"); }, ({ status, error }) => { expect(status).equals("no-exchange-found"); expect(error).undefined; }, ], TestingContext, ); expect(hookBehavior).deep.equal({ result: "ok" }); expect(handler.getCallingQueueState()).eq("empty"); }); it("should be able to withdraw if tos are ok", async () => { const { handler, TestingContext } = createWalletApiMock(); const props = { talerWithdrawUri: "taler-withdraw://", cancel: nullFunction, onSuccess: nullFunction, }; handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForUri, undefined, { status: "pending", operationId: "123", amount: "ARS:2" as AmountString, possibleExchanges: exchanges, defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, }, ); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalTransactionByUri, undefined, { transactionId: "123" } as any, ); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForAmount, undefined, { amountRaw: "ARS:2" as AmountString, amountEffective: "ARS:2" as AmountString, paytoUris: ["payto://"], tosAccepted: true, scopeInfo: { currency: "ARS", type: ScopeType.Exchange, url: "http://asd" }, withdrawalAccountsList: [], ageRestrictionOptions: [], numCoins: 42, }, ); const hookBehavior = await tests.hookBehaveLikeThis( useComponentStateFromURI, props, [ ({ status }) => { expect(status).equals("loading"); }, ({ status, error }) => { expect(status).equals("loading"); expect(error).undefined; }, (state) => { expect(state.status).equals("success"); if (state.status !== "success") return; expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2")); expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0")); expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2")); expect(state.doWithdrawal.onClick).not.undefined; }, ], TestingContext, ); expect(hookBehavior).deep.equal({ result: "ok" }); expect(handler.getCallingQueueState()).eq("empty"); }); it.skip("should accept the tos before withdraw", async () => { const { handler, TestingContext } = createWalletApiMock(); const props = { talerWithdrawUri: "taler-withdraw://", cancel: nullFunction, onSuccess: nullFunction, }; const exchangeWithNewTos = exchanges.map((e) => ({ ...e, tosStatus: ExchangeTosStatus.Proposed, })); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForUri, undefined, { status: "pending", operationId: "123", amount: "ARS:2" as AmountString, possibleExchanges: exchangeWithNewTos, defaultExchangeBaseUrl: exchangeWithNewTos[0].exchangeBaseUrl, }, ); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForAmount, undefined, { amountRaw: "ARS:2" as AmountString, amountEffective: "ARS:2" as AmountString, paytoUris: ["payto://"], scopeInfo: { currency: "ARS", type: ScopeType.Exchange, url: "http://asd" }, tosAccepted: false, withdrawalAccountsList: [], ageRestrictionOptions: [], numCoins: 42, }, ); handler.addWalletCallResponse( WalletApiOperation.GetWithdrawalDetailsForUri, undefined, { status: "pending", operationId: "123", amount: "ARS:2" as AmountString, possibleExchanges: exchanges, defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, }, ); const hookBehavior = await tests.hookBehaveLikeThis( useComponentStateFromURI, props, [ ({ status }) => { expect(status).equals("loading"); }, ({ status, error }) => { expect(status).equals("loading"); expect(error).undefined; }, (state) => { expect(state.status).equals("success"); if (state.status !== "success") return; expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2")); expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0")); expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2")); expect(state.doWithdrawal.onClick).not.undefined; }, ], TestingContext, ); expect(hookBehavior).deep.equal({ result: "ok" }); expect(handler.getCallingQueueState()).eq("empty"); }); });