gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: fix #7496 with unit tests


From: gnunet
Subject: [taler-wallet-core] branch master updated: fix #7496 with unit tests
Date: Mon, 28 Nov 2022 13:29:25 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 3577227cc fix #7496 with unit tests
3577227cc is described below

commit 3577227cc0ff0f9e0c422ae34c4407d88e98ec21
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Nov 28 09:29:14 2022 -0300

    fix #7496 with unit tests
---
 .../src/components/TermsOfService/test.ts          |   4 +-
 .../src/cta/InvoiceCreate/state.ts                 |   3 +-
 .../src/cta/InvoiceCreate/test.ts                  |   4 +-
 .../src/cta/InvoicePay/test.ts                     |   4 +-
 .../src/cta/TransferCreate/test.ts                 |   4 +-
 .../src/cta/TransferPickup/test.ts                 |   4 +-
 .../src/cta/Withdraw/state.ts                      |   3 +-
 .../taler-wallet-webextension/src/utils/index.ts   |   2 +-
 .../src/wallet/Application.tsx                     |  12 +-
 .../src/wallet/DestinationSelection/index.ts       |  93 +++++
 .../src/wallet/DestinationSelection/state.ts       | 167 +++++++++
 .../stories.tsx}                                   |  41 ++-
 .../src/wallet/DestinationSelection/test.ts        | 136 ++++++++
 .../views.tsx}                                     | 379 +++++++--------------
 .../src/wallet/ManageAccount/test.ts               |   4 +-
 .../src/wallet/Notifications/test.ts               |   4 +-
 .../src/wallet/index.stories.tsx                   |   2 +-
 17 files changed, 576 insertions(+), 290 deletions(-)

diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/test.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/test.ts
index eae4d4ca2..170e7cad8 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/test.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Term of service states", () => {
+  it.skip("should assert", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index 4ce81dad8..6007b5193 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -25,11 +25,10 @@ import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
+import { RecursiveState } from "../../utils/index.js";
 import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
-type RecursiveState<S extends object> = S | (() => RecursiveState<S>);
-
 export function useComponentState(
   { amount: amountStr, onClose, onSuccess }: Props,
   api: typeof wxApi,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/test.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/test.ts
index eae4d4ca2..3ebedfd5a 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Invoice create state", () => {
+  it.skip("should create some tests", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/test.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/test.ts
index eae4d4ca2..4a3d08ed0 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Invoice payment state", () => {
+  it.skip("should create some states", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/test.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/test.ts
index eae4d4ca2..be753e492 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Transfer create states", () => {
+  it.skip("should assert", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/test.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/test.ts
index eae4d4ca2..fa5b6979a 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Transfer pickup states", () => {
+  it.skip("should assert", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 016027163..9bb29fbd6 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -25,11 +25,10 @@ import { TalerError, WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
+import { RecursiveState } from "../../utils/index.js";
 import { wxApi } from "../../wxApi.js";
 import { PropsFromParams, PropsFromURI, State } from "./index.js";
 
-type RecursiveState<S extends object> = S | (() => RecursiveState<S>);
-
 export function useComponentStateFromParams(
   { amount, cancel, onSuccess }: PropsFromParams,
   api: typeof wxApi,
diff --git a/packages/taler-wallet-webextension/src/utils/index.ts 
b/packages/taler-wallet-webextension/src/utils/index.ts
index 23cfc7730..c2d7c10a8 100644
--- a/packages/taler-wallet-webextension/src/utils/index.ts
+++ b/packages/taler-wallet-webextension/src/utils/index.ts
@@ -80,7 +80,7 @@ export type StateViewMap<StateType extends { status: string 
}> = {
   [S in StateType as S["status"]]: StateFunc<S>;
 };
 
-type RecursiveState<S extends object> = S | (() => RecursiveState<S>);
+export type RecursiveState<S extends object> = S | (() => RecursiveState<S>);
 
 export function compose<SType extends { status: string }, PType>(
   name: string,
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 6362f1924..d150ebfaf 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -48,17 +48,13 @@ import { BackupPage } from "./BackupPage.js";
 import { DepositPage } from "./DepositPage/index.js";
 import { ExchangeAddPage } from "./ExchangeAddPage.js";
 import { HistoryPage } from "./History.js";
-import { ProviderAddPage } from "./ProviderAddPage.js";
 import { ProviderDetailPage } from "./ProviderDetailPage.js";
 import { SettingsPage } from "./Settings.js";
 import { TransactionPage } from "./Transaction.js";
 import { WelcomePage } from "./Welcome.js";
 import { QrReaderPage } from "./QrReader.js";
 import { platform } from "../platform/api.js";
-import {
-  DestinationSelectionGetCash,
-  DestinationSelectionSendCash,
-} from "./DestinationSelection.js";
+import { DestinationSelectionPage } from "./DestinationSelection/index.js";
 import { ExchangeSelectionPage } from "./ExchangeSelection/index.js";
 import { TransferCreatePage } from "../cta/TransferCreate/index.js";
 import { InvoiceCreatePage } from "../cta/InvoiceCreate/index.js";
@@ -153,7 +149,8 @@ export function Application(): VNode {
               <Route path={Pages.exchanges} component={ExchangeSelectionPage} 
/>
               <Route
                 path={Pages.sendCash.pattern}
-                component={DestinationSelectionSendCash}
+                type="send"
+                component={DestinationSelectionPage}
                 goToWalletBankDeposit={(amount: string) =>
                   redirectTo(Pages.balanceDeposit({ amount }))
                 }
@@ -163,7 +160,8 @@ export function Application(): VNode {
               />
               <Route
                 path={Pages.receiveCash.pattern}
-                component={DestinationSelectionGetCash}
+                type="get"
+                component={DestinationSelectionPage}
                 goToWalletManualWithdraw={(amount?: string) =>
                   redirectTo(Pages.ctaWithdrawManual({ amount }))
                 }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
new file mode 100644
index 000000000..492da193b
--- /dev/null
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
@@ -0,0 +1,93 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+import { Loading } from "../../components/Loading.js";
+import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { AmountFieldHandler, ButtonHandler } from "../../mui/handlers.js";
+import { compose, StateViewMap } from "../../utils/index.js";
+import { wxApi } from "../../wxApi.js";
+import { useComponentState } from "./state.js";
+import { LoadingUriView, ReadyView, SelectCurrencyView } from "./views.js";
+
+export type Props = PropsGet | PropsSend;
+
+interface PropsGet {
+  type: "get";
+  amount?: string;
+  goToWalletManualWithdraw: (amount: string) => void;
+  goToWalletWalletInvoice: (amount: string) => void;
+}
+interface PropsSend {
+  type: "send";
+  amount?: string;
+  goToWalletBankDeposit: (amount: string) => void;
+  goToWalletWalletSend: (amount: string) => void;
+}
+
+export type State =
+  | State.Loading
+  | State.LoadingUriError
+  | State.Ready
+  | State.SelectCurrency;
+
+export namespace State {
+  export interface Loading {
+    status: "loading";
+    error: undefined;
+  }
+
+  export interface LoadingUriError {
+    status: "loading-error";
+    error: HookError;
+  }
+
+  export interface SelectCurrency {
+    status: "select-currency";
+    error: undefined;
+    currencies: Record<string, string>;
+    onCurrencySelected: (currency: string) => void;
+  }
+
+  export interface Ready {
+    status: "ready";
+    error: undefined;
+    type: Props["type"];
+    selectCurrency: ButtonHandler;
+    previous: Contact[];
+    goToBank: ButtonHandler;
+    goToWallet: ButtonHandler;
+    amountHandler: AmountFieldHandler;
+  }
+}
+
+export type Contact = {
+  icon: string;
+  name: string;
+  description: string;
+};
+
+const viewMapping: StateViewMap<State> = {
+  loading: Loading,
+  "loading-error": LoadingUriView,
+  "select-currency": SelectCurrencyView,
+  ready: ReadyView,
+};
+
+export const DestinationSelectionPage = compose(
+  "DestinationSelectionPage",
+  (p: Props) => useComponentState(p, wxApi),
+  viewMapping,
+);
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
new file mode 100644
index 000000000..fe02151de
--- /dev/null
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -0,0 +1,167 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+import { Amounts } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useState } from "preact/hooks";
+import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
+import { assertUnreachable, RecursiveState } from "../../utils/index.js";
+import { wxApi } from "../../wxApi.js";
+import { Contact, Props, State } from "./index.js";
+import bankIcon from "../../svg/ri-bank-line.svg";
+
+export function useComponentState(
+  props: Props,
+  api: typeof wxApi,
+): RecursiveState<State> {
+  const parsedInitialAmount = !props.amount
+    ? undefined
+    : Amounts.parse(props.amount);
+
+  // const initialCurrency = parsedInitialAmount?.currency;
+
+  const [amount, setAmount] = useState(
+    !parsedInitialAmount ? undefined : parsedInitialAmount,
+  );
+
+  //FIXME: get this information from wallet
+  // eslint-disable-next-line no-constant-condition
+  const previous: Contact[] = true
+    ? []
+    : [
+        {
+          name: "International Bank",
+          icon: bankIcon, //FIXME: should be decided in the view
+          description: "account ending with 3454",
+        },
+        {
+          name: "Max",
+          icon: bankIcon,
+          description: "account ending with 3454",
+        },
+        {
+          name: "Alex",
+          icon: bankIcon,
+          description: "account ending with 3454",
+        },
+      ];
+
+  if (!amount) {
+    return () => {
+      // eslint-disable-next-line react-hooks/rules-of-hooks
+      const hook = useAsyncAsHook(() =>
+        api.wallet.call(WalletApiOperation.ListExchanges, {}),
+      );
+
+      if (!hook) {
+        return {
+          status: "loading",
+          error: undefined,
+        };
+      }
+      if (hook.hasError) {
+        return {
+          status: "loading-error",
+          error: hook,
+        };
+      }
+      const currencies: Record<string, string> = {};
+      hook.response.exchanges.forEach((e) => {
+        if (e.currency) {
+          currencies[e.currency] = e.currency;
+        }
+      });
+      currencies[""] = "Select a currency";
+
+      return {
+        status: "select-currency",
+        error: undefined,
+        onCurrencySelected: (c: string) => {
+          setAmount(Amounts.zeroOfCurrency(c));
+        },
+        currencies,
+      };
+    };
+  }
+
+  const currencyAndAmount = Amounts.stringify(amount);
+  const invalid = Amounts.isZero(amount);
+
+  switch (props.type) {
+    case "send":
+      return {
+        status: "ready",
+        error: undefined,
+        previous,
+        selectCurrency: {
+          onClick: async () => {
+            setAmount(undefined);
+          },
+        },
+        goToBank: {
+          onClick: invalid
+            ? undefined
+            : async () => {
+                props.goToWalletBankDeposit(currencyAndAmount);
+              },
+        },
+        goToWallet: {
+          onClick: invalid
+            ? undefined
+            : async () => {
+                props.goToWalletWalletSend(currencyAndAmount);
+              },
+        },
+        amountHandler: {
+          onInput: async (s) => setAmount(s),
+          value: amount,
+        },
+        type: props.type,
+      };
+    case "get":
+      return {
+        status: "ready",
+        error: undefined,
+        previous,
+        selectCurrency: {
+          onClick: async () => {
+            setAmount(undefined);
+          },
+        },
+        goToBank: {
+          onClick: invalid
+            ? undefined
+            : async () => {
+                props.goToWalletManualWithdraw(currencyAndAmount);
+              },
+        },
+        goToWallet: {
+          onClick: invalid
+            ? undefined
+            : async () => {
+                props.goToWalletWalletInvoice(currencyAndAmount);
+              },
+        },
+        amountHandler: {
+          onInput: async (s) => setAmount(s),
+          value: amount,
+        },
+        type: props.type,
+      };
+    default:
+      assertUnreachable(props);
+  }
+}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
similarity index 60%
rename from 
packages/taler-wallet-webextension/src/wallet/DestinationSelection.stories.tsx
rename to 
packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
index 166b2c007..b8d868683 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
@@ -19,25 +19,44 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
-import {
-  DestinationSelectionGetCash,
-  DestinationSelectionSendCash,
-  SelectCurrencyView,
-} from "./DestinationSelection.js";
+import { createExample } from "../../test-utils.js";
+import { ReadyView, SelectCurrencyView } from "./views.js";
 
 export default {
   title: "wallet/destination",
 };
 
-export const GetCash = createExample(DestinationSelectionGetCash, {
-  amount: "usd:0",
+export const GetCash = createExample(ReadyView, {
+  amountHandler: {
+    value: {
+      currency: "EUR",
+      fraction: 0,
+      value: 2,
+    },
+  },
+  goToBank: {},
+  goToWallet: {},
+  previous: [],
+  selectCurrency: {},
+  type: "get",
 });
-export const SendCash = createExample(DestinationSelectionSendCash, {
-  amount: "eur:1",
+export const SendCash = createExample(ReadyView, {
+  amountHandler: {
+    value: {
+      currency: "EUR",
+      fraction: 0,
+      value: 1,
+    },
+  },
+  goToBank: {},
+  goToWallet: {},
+  previous: [],
+  selectCurrency: {},
+  type: "send",
 });
+
 export const SelectCurrency = createExample(SelectCurrencyView, {
-  list: {
+  currencies: {
     "": "Select a currency",
     USD: "USD",
   },
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
new file mode 100644
index 000000000..c2aa04849
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
@@ -0,0 +1,136 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import {
+  Amounts,
+  ExchangeEntryStatus,
+  ExchangeListItem,
+  ExchangeTosStatus,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { expect } from "chai";
+import { createWalletApiMock, mountHook } from "../../test-utils.js";
+import { useComponentState } from "./state.js";
+
+const exchangeArs: ExchangeListItem = {
+  currency: "ARS",
+  exchangeBaseUrl: "http://";,
+  tosStatus: ExchangeTosStatus.Accepted,
+  exchangeStatus: ExchangeEntryStatus.Ok,
+  paytoUris: [],
+  permanent: true,
+  ageRestrictionOptions: [],
+};
+
+describe("Destination selection states", () => {
+  it("should select currency if no amount specified", async () => {
+    const { handler, mock } = createWalletApiMock();
+
+    handler.addWalletCallResponse(
+      WalletApiOperation.ListExchanges,
+      {},
+      {
+        exchanges: [exchangeArs],
+      },
+    );
+
+    const props = {
+      type: "get" as const,
+      goToWalletManualWithdraw: () => {
+        return null;
+      },
+      goToWalletWalletInvoice: () => {
+        null;
+      },
+    };
+    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
+      mountHook(() => useComponentState(props, mock));
+
+    {
+      const state = pullLastResultOrThrow();
+
+      if (state.status !== "loading") expect.fail();
+      if (state.error) expect.fail();
+    }
+
+    expect(await waitForStateUpdate()).true;
+
+    {
+      const state = pullLastResultOrThrow();
+
+      if (state.status !== "select-currency") expect.fail();
+      if (state.error) expect.fail();
+      expect(state.currencies).deep.eq({
+        ARS: "ARS",
+        "": "Select a currency",
+      });
+
+      state.onCurrencySelected(exchangeArs.currency!);
+    }
+
+    expect(await waitForStateUpdate()).true;
+
+    {
+      const state = pullLastResultOrThrow();
+
+      if (state.status !== "ready") expect.fail();
+      if (state.error) expect.fail();
+      expect(state.goToBank.onClick).eq(undefined);
+      expect(state.goToWallet.onClick).eq(undefined);
+
+      expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0"));
+    }
+
+    await assertNoPendingUpdate();
+    expect(handler.getCallingQueueState()).eq("empty");
+  });
+
+  it("should be possible to start with an amount specified in request params", 
async () => {
+    const { handler, mock } = createWalletApiMock();
+
+    const props = {
+      type: "get" as const,
+      goToWalletManualWithdraw: () => {
+        return null;
+      },
+      goToWalletWalletInvoice: () => {
+        null;
+      },
+      amount: "ARS:2",
+    };
+    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
+      mountHook(() => useComponentState(props, mock));
+
+    {
+      const state = pullLastResultOrThrow();
+
+      if (state.status !== "ready") expect.fail();
+      if (state.error) expect.fail();
+      expect(state.goToBank.onClick).not.eq(undefined);
+      expect(state.goToWallet.onClick).not.eq(undefined);
+
+      expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:2"));
+    }
+
+    await assertNoPendingUpdate();
+    expect(handler.getCallingQueueState()).eq("empty");
+  });
+});
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
similarity index 61%
rename from 
packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
rename to 
packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
index 7e4c775e6..ba8d65ffa 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
@@ -14,30 +14,70 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { AmountField } from "../components/AmountField.js";
-import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
-import { SelectList } from "../components/SelectList.js";
+import { LoadingError } from "../../components/LoadingError.js";
+import { SelectList } from "../../components/SelectList.js";
 import {
   Input,
   LightText,
   LinkPrimary,
   SvgIcon,
-} from "../components/styled/index.js";
-import { useTranslationContext } from "../context/translation.js";
-import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
-import { Button } from "../mui/Button.js";
-import { Grid } from "../mui/Grid.js";
-import { Paper } from "../mui/Paper.js";
-import { Pages } from "../NavigationBar.js";
-import arrowIcon from "../svg/chevron-down.svg";
-import bankIcon from "../svg/ri-bank-line.svg";
-import { wxApi } from "../wxApi.js";
+} from "../../components/styled/index.js";
+import { useTranslationContext } from "../../context/translation.js";
+import { Pages } from "../../NavigationBar.js";
+import { Contact, State } from "./index.js";
+import arrowIcon from "../../svg/chevron-down.svg";
+import { AmountField } from "../../components/AmountField.js";
+import { Grid } from "../../mui/Grid.js";
+import { Paper } from "../../mui/Paper.js";
+import { Button } from "../../mui/Button.js";
+import { assertUnreachable } from "../../utils/index.js";
+
+export function LoadingUriView({ error }: State.LoadingUriError): VNode {
+  const { i18n } = useTranslationContext();
+  return (
+    <LoadingError
+      title={<i18n.Translate>Could not load</i18n.Translate>}
+      error={error}
+    />
+  );
+}
+
+export function SelectCurrencyView({
+  currencies,
+  onCurrencySelected,
+}: State.SelectCurrency): VNode {
+  const { i18n } = useTranslationContext();
+
+  return (
+    <Fragment>
+      <h2>
+        <i18n.Translate>
+          Choose a currency to proceed or add another exchange
+        </i18n.Translate>
+      </h2>
+
+      <p>
+        <Input>
+          <SelectList
+            label={<i18n.Translate>Known currencies</i18n.Translate>}
+            list={currencies}
+            name="lang"
+            value={""}
+            onChange={(v) => onCurrencySelected(v)}
+          />
+        </Input>
+      </p>
+      <div style={{ display: "flex", justifyContent: "space-between" }}>
+        <div />
+        <LinkPrimary href={Pages.settingsExchangeAdd({})}>
+          <i18n.Translate>Add an exchange</i18n.Translate>
+        </LinkPrimary>
+      </div>
+    </Fragment>
+  );
+}
 
 const Container = styled.div`
   display: flex;
@@ -47,23 +87,6 @@ const Container = styled.div`
   }
 `;
 
-interface PropsGet {
-  amount?: string;
-  goToWalletManualWithdraw: (amount: string) => void;
-  goToWalletWalletInvoice: (amount: string) => void;
-}
-interface PropsSend {
-  amount?: string;
-  goToWalletBankDeposit: (amount: string) => void;
-  goToWalletWalletSend: (amount: string) => void;
-}
-
-type Contact = {
-  icon: string;
-  name: string;
-  description: string;
-};
-
 const ContactTable = styled.table`
   width: 100%;
   & > tr > td {
@@ -165,155 +188,25 @@ const CircleDiv = styled.div`
   border: none;
 `;
 
-export function SelectCurrency({
-  onChange,
-}: {
-  onChange: (s: string) => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-
-  const hook = useAsyncAsHook(() =>
-    wxApi.wallet.call(WalletApiOperation.ListExchanges, {}),
-  );
-
-  if (!hook) {
-    return <Loading />;
+export function ReadyView(props: State.Ready): VNode {
+  switch (props.type) {
+    case "get":
+      return ReadyGetView(props);
+    case "send":
+      return ReadySendView(props);
+    default:
+      assertUnreachable(props.type);
   }
-  if (hook.hasError) {
-    return (
-      <LoadingError
-        error={hook}
-        title={<i18n.Translate>Could not load list of 
exchange</i18n.Translate>}
-      />
-    );
-  }
-  const list: Record<string, string> = {};
-  hook.response.exchanges.forEach((e) => {
-    if (e.currency) {
-      list[e.currency] = e.currency;
-    }
-  });
-  list[""] = "Select a currency";
-  return <SelectCurrencyView onChange={onChange} list={list} />;
 }
-
-export function SelectCurrencyView({
-  onChange,
-  list,
-}: {
-  onChange: (s: string) => void;
-  list: Record<string, string>;
-}): VNode {
+export function ReadyGetView({
+  amountHandler,
+  goToBank,
+  goToWallet,
+  selectCurrency,
+  previous,
+}: State.Ready): VNode {
   const { i18n } = useTranslationContext();
 
-  return (
-    <Fragment>
-      <h2>
-        <i18n.Translate>
-          Choose a currency to proceed or add another exchange
-        </i18n.Translate>
-      </h2>
-
-      <p>
-        <Input>
-          <SelectList
-            label={<i18n.Translate>Known currencies</i18n.Translate>}
-            list={list}
-            name="lang"
-            value={""}
-            onChange={(v) => onChange(v)}
-          />
-        </Input>
-      </p>
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <div />
-        <LinkPrimary href={Pages.settingsExchangeAdd({})}>
-          <i18n.Translate>Add an exchange</i18n.Translate>
-        </LinkPrimary>
-      </div>
-    </Fragment>
-  );
-}
-
-function RowExample({
-  info,
-  disabled,
-}: {
-  info: Contact;
-  disabled?: boolean;
-}): VNode {
-  return (
-    <MediaExample data-disabled={disabled}>
-      <MediaLeft>
-        <CircleDiv>
-          <SvgIcon
-            title={info.name}
-            dangerouslySetInnerHTML={{ __html: info.icon }}
-            color="currentColor"
-          />
-        </CircleDiv>
-      </MediaLeft>
-      <MediaBody>
-        <span>{info.name}</span>
-        <LightText>{info.description}</LightText>
-      </MediaBody>
-      <MediaRight>
-        <SvgIcon
-          title="Select this contact"
-          dangerouslySetInnerHTML={{ __html: arrowIcon }}
-          color="currentColor"
-          transform="rotate(-90deg)"
-        />
-      </MediaRight>
-    </MediaExample>
-  );
-}
-
-export function DestinationSelectionGetCash({
-  amount: initialAmount,
-  goToWalletManualWithdraw,
-  goToWalletWalletInvoice,
-}: PropsGet): VNode {
-  const parsedInitialAmount = !initialAmount
-    ? undefined
-    : Amounts.parse(initialAmount);
-
-  const [currency, setCurrency] = useState(parsedInitialAmount?.currency);
-
-  const [amount, setAmount] = useState(
-    parsedInitialAmount ?? Amounts.zeroOfCurrency(currency ?? "KUDOS"),
-  );
-
-  const { i18n } = useTranslationContext();
-  const previous1: Contact[] = [];
-  const previous2: Contact[] = [
-    {
-      name: "International Bank",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-    {
-      name: "Max",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-    {
-      name: "Alex",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-  ];
-  const previous = previous1;
-
-  if (!currency) {
-    return (
-      <div>
-        <SelectCurrency onChange={(c) => setCurrency(c)} />
-      </div>
-    );
-  }
-  const currencyAndAmount = Amounts.stringify(amount);
-  const invalid = Amounts.isZero(amount);
   return (
     <Container>
       <h1>
@@ -323,12 +216,9 @@ export function DestinationSelectionGetCash({
         <AmountField
           label={<i18n.Translate>Amount</i18n.Translate>}
           required
-          handler={{
-            onInput: async (s) => setAmount(s),
-            value: amount,
-          }}
+          handler={amountHandler}
         />
-        <Button onClick={async () => setCurrency(undefined)}>
+        <Button onClick={selectCurrency.onClick}>
           <i18n.Translate>Change currency</i18n.Translate>
         </Button>
       </Grid>
@@ -345,7 +235,10 @@ export function DestinationSelectionGetCash({
                   {previous.map((info, i) => (
                     <tr key={i}>
                       <td>
-                        <RowExample info={info} disabled={invalid} />
+                        <RowExample
+                          info={info}
+                          disabled={!amountHandler.onInput}
+                        />
                       </td>
                     </tr>
                   ))}
@@ -375,12 +268,7 @@ export function DestinationSelectionGetCash({
               <p>
                 <i18n.Translate>From my bank account</i18n.Translate>
               </p>
-              <Button
-                disabled={invalid}
-                onClick={async () =>
-                  goToWalletManualWithdraw(currencyAndAmount)
-                }
-              >
+              <Button onClick={goToBank.onClick}>
                 <i18n.Translate>Withdraw</i18n.Translate>
               </Button>
             </Paper>
@@ -390,10 +278,7 @@ export function DestinationSelectionGetCash({
               <p>
                 <i18n.Translate>From another wallet</i18n.Translate>
               </p>
-              <Button
-                disabled={invalid}
-                onClick={async () => 
goToWalletWalletInvoice(currencyAndAmount)}
-              >
+              <Button onClick={goToWallet.onClick}>
                 <i18n.Translate>Invoice</i18n.Translate>
               </Button>
             </Paper>
@@ -403,52 +288,14 @@ export function DestinationSelectionGetCash({
     </Container>
   );
 }
-
-export function DestinationSelectionSendCash({
-  amount: initialAmount,
-  goToWalletBankDeposit,
-  goToWalletWalletSend,
-}: PropsSend): VNode {
-  const parsedInitialAmount = !initialAmount
-    ? undefined
-    : Amounts.parse(initialAmount);
-
-  const currency = parsedInitialAmount?.currency;
-
-  const [amount, setAmount] = useState(
-    parsedInitialAmount ?? Amounts.zeroOfCurrency(currency ?? "KUDOS"),
-  );
+export function ReadySendView({
+  amountHandler,
+  goToBank,
+  goToWallet,
+  previous,
+}: State.Ready): VNode {
   const { i18n } = useTranslationContext();
-  const previous1: Contact[] = [];
-  const previous2: Contact[] = [
-    {
-      name: "International Bank",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-    {
-      name: "Max",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-    {
-      name: "Alex",
-      icon: bankIcon,
-      description: "account ending with 3454",
-    },
-  ];
-  const previous = previous1;
 
-  if (!currency) {
-    return (
-      <div>
-        <i18n.Translate>currency not provided</i18n.Translate>
-      </div>
-    );
-  }
-  const currencyAndAmount = Amounts.stringify(amount);
-  //const parsedAmount = Amounts.parse(currencyAndAmount);
-  const invalid = Amounts.isZero(amount);
   return (
     <Container>
       <h1>
@@ -459,10 +306,7 @@ export function DestinationSelectionSendCash({
         <AmountField
           label={<i18n.Translate>Amount</i18n.Translate>}
           required
-          handler={{
-            onInput: async (s) => setAmount(s),
-            value: amount,
-          }}
+          handler={amountHandler}
         />
       </div>
 
@@ -478,7 +322,10 @@ export function DestinationSelectionSendCash({
                   {previous.map((info, i) => (
                     <tr key={i}>
                       <td>
-                        <RowExample info={info} disabled={invalid} />
+                        <RowExample
+                          info={info}
+                          disabled={!amountHandler.onInput}
+                        />
                       </td>
                     </tr>
                   ))}
@@ -510,10 +357,7 @@ export function DestinationSelectionSendCash({
               <p>
                 <i18n.Translate>To my bank account</i18n.Translate>
               </p>
-              <Button
-                disabled={invalid}
-                onClick={async () => goToWalletBankDeposit(currencyAndAmount)}
-              >
+              <Button onClick={goToBank.onClick}>
                 <i18n.Translate>Deposit</i18n.Translate>
               </Button>
             </Paper>
@@ -523,10 +367,7 @@ export function DestinationSelectionSendCash({
               <p>
                 <i18n.Translate>To another wallet</i18n.Translate>
               </p>
-              <Button
-                disabled={invalid}
-                onClick={async () => goToWalletWalletSend(currencyAndAmount)}
-              >
+              <Button onClick={goToWallet.onClick}>
                 <i18n.Translate>Send</i18n.Translate>
               </Button>
             </Paper>
@@ -536,3 +377,37 @@ export function DestinationSelectionSendCash({
     </Container>
   );
 }
+
+function RowExample({
+  info,
+  disabled,
+}: {
+  info: Contact;
+  disabled?: boolean;
+}): VNode {
+  return (
+    <MediaExample data-disabled={disabled}>
+      <MediaLeft>
+        <CircleDiv>
+          <SvgIcon
+            title={info.name}
+            dangerouslySetInnerHTML={{ __html: info.icon }}
+            color="currentColor"
+          />
+        </CircleDiv>
+      </MediaLeft>
+      <MediaBody>
+        <span>{info.name}</span>
+        <LightText>{info.description}</LightText>
+      </MediaBody>
+      <MediaRight>
+        <SvgIcon
+          title="Select this contact"
+          dangerouslySetInnerHTML={{ __html: arrowIcon }}
+          color="currentColor"
+          transform="rotate(-90deg)"
+        />
+      </MediaRight>
+    </MediaExample>
+  );
+}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts
index eae4d4ca2..868269ec0 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Manage Account states", () => {
+  it.skip("should create some tests", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/test.ts 
b/packages/taler-wallet-webextension/src/wallet/Notifications/test.ts
index eae4d4ca2..c4ce1efc7 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/test.ts
@@ -21,8 +21,8 @@
 
 import { expect } from "chai";
 
-describe("test description", () => {
-  it("should assert", () => {
+describe("Notifications states", () => {
+  it.skip("should create some tests", () => {
     expect([]).deep.equals([]);
   });
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
index 20de1a3c3..554527108 100644
--- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
@@ -33,7 +33,7 @@ import * as a14 from "./Welcome.stories.js";
 import * as a15 from "./AddNewActionView.stories.js";
 import * as a16 from "./DeveloperPage.stories.js";
 import * as a17 from "./QrReader.stories.js";
-import * as a18 from "./DestinationSelection.stories.js";
+import * as a18 from "./DestinationSelection/stories.js";
 import * as a19 from "./ExchangeSelection/stories.js";
 import * as a20 from "./ManageAccount/stories.js";
 import * as a21 from "./Notifications/stories.js";

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]