gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wxApi from context and using


From: gnunet
Subject: [taler-wallet-core] branch master updated: wxApi from context and using the new testing sdk
Date: Thu, 15 Dec 2022 21:11:38 +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 f93bd5149 wxApi from context and using the new testing sdk
f93bd5149 is described below

commit f93bd51499ed34844b666bf6d333227adf4368bf
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Dec 15 17:11:24 2022 -0300

    wxApi from context and using the new testing sdk
---
 packages/taler-wallet-webextension/package.json    |   4 +-
 .../src/NavigationBar.tsx                          |  21 +-
 .../src/components/PendingTransactions.tsx         |   7 +-
 .../src/components/ShowFullContractTermPopup.tsx   |   7 +-
 .../src/components/TermsOfService/index.ts         |   5 +-
 .../src/components/TermsOfService/state.ts         |   4 +-
 .../src/context/backend.ts                         |  53 +++
 .../src/cta/Deposit/index.ts                       |   3 +-
 .../src/cta/Deposit/state.ts                       |   4 +-
 .../src/cta/Deposit/test.ts                        |  80 ++--
 .../src/cta/InvoiceCreate/index.ts                 |   3 +-
 .../src/cta/InvoiceCreate/state.ts                 |   8 +-
 .../src/cta/InvoicePay/index.ts                    |   5 +-
 .../src/cta/InvoicePay/state.ts                    |   4 +-
 .../src/cta/Payment/index.ts                       |   5 +-
 .../src/cta/Payment/state.ts                       |   4 +-
 .../src/cta/Payment/test.ts                        | 404 +++++++++------------
 .../src/cta/Recovery/index.ts                      |   3 +-
 .../src/cta/Recovery/state.ts                      |   6 +-
 .../src/cta/Refund/index.ts                        |   5 +-
 .../src/cta/Refund/state.ts                        |   4 +-
 .../src/cta/Refund/test.ts                         | 349 +++++-------------
 .../taler-wallet-webextension/src/cta/Tip/index.ts |   5 +-
 .../taler-wallet-webextension/src/cta/Tip/state.ts |   4 +-
 .../taler-wallet-webextension/src/cta/Tip/test.ts  | 271 ++++++--------
 .../src/cta/TransferCreate/index.ts                |   3 +-
 .../src/cta/TransferCreate/state.ts                |  12 +-
 .../src/cta/TransferPickup/index.ts                |   5 +-
 .../src/cta/TransferPickup/state.ts                |   6 +-
 .../src/cta/Withdraw/index.ts                      |   7 +-
 .../src/cta/Withdraw/state.ts                      |  20 +-
 .../src/cta/Withdraw/test.ts                       | 230 +++++-------
 .../src/hooks/useAutoOpenPermissions.ts            |  12 +-
 .../src/hooks/useBackupDeviceName.ts               |   7 +-
 .../src/hooks/useClipboardPermissions.ts           |  12 +-
 .../src/hooks/useDiagnostics.ts                    |   5 +-
 .../src/hooks/useProviderStatus.ts                 |  10 +-
 .../src/hooks/useWalletDevMode.ts                  |  11 +-
 .../src/popup/BalancePage.tsx                      |  14 +-
 .../taler-wallet-webextension/src/test-utils.ts    |  14 +-
 .../src/wallet/AddBackupProvider/index.ts          |  14 +-
 .../src/wallet/AddBackupProvider/state.ts          |  68 ++--
 .../src/wallet/AddBackupProvider/test.ts           |  57 +--
 .../src/wallet/BackupPage.tsx                      |   9 +-
 .../src/wallet/DepositPage/index.ts                |   8 +-
 .../src/wallet/DepositPage/state.ts                |  24 +-
 .../src/wallet/DepositPage/test.ts                 | 373 +++++++------------
 .../src/wallet/DestinationSelection/index.ts       |   3 +-
 .../src/wallet/DestinationSelection/state.ts       |   4 +-
 .../src/wallet/DestinationSelection/test.ts        | 113 +++---
 .../src/wallet/DeveloperPage.tsx                   |  24 +-
 .../src/wallet/EmptyComponentExample/index.ts      |   3 +-
 .../src/wallet/EmptyComponentExample/state.ts      |   3 +-
 .../src/wallet/ExchangeAddPage.tsx                 |   7 +-
 .../src/wallet/ExchangeSelection/index.ts          |   7 +-
 .../src/wallet/ExchangeSelection/state.ts          |  14 +-
 .../src/wallet/History.tsx                         |   9 +-
 .../src/wallet/ManageAccount/index.ts              |   5 +-
 .../src/wallet/ManageAccount/state.ts              |   6 +-
 .../src/wallet/Notifications/index.ts              |   5 +-
 .../src/wallet/Notifications/state.ts              |   5 +-
 .../src/wallet/ProviderAddPage.tsx                 |   6 +-
 .../src/wallet/ProviderDetailPage.tsx              |  12 +-
 .../src/wallet/Settings.tsx                        |   7 +-
 .../src/wallet/Transaction.tsx                     |  14 +-
 packages/taler-wallet-webextension/src/wxApi.ts    |   8 +
 packages/web-util/src/tests/hook.ts                |   2 +-
 67 files changed, 1002 insertions(+), 1444 deletions(-)

diff --git a/packages/taler-wallet-webextension/package.json 
b/packages/taler-wallet-webextension/package.json
index 0deca26cc..5c6983bfc 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -9,7 +9,7 @@
   "private": false,
   "scripts": {
     "clean": "rimraf dist lib tsconfig.tsbuildinfo",
-    "test": "pnpm compile && mocha 'dist/**/*.test.js' 'dist/**/test.js'",
+    "test": "pnpm compile && mocha --require source-map-support/register 
'dist/**/*.test.js' 'dist/**/test.js'",
     "test:coverage": "nyc pnpm test",
     "compile": "tsc && ./build-fast-with-linaria.mjs",
     "prepare": "pnpm compile",
@@ -80,4 +80,4 @@
   "pogen": {
     "domain": "taler-wallet-webex"
   }
-}
\ No newline at end of file
+}
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index 8fb289aa6..ab36af376 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -24,20 +24,20 @@
 /**
  * Imports.
  */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
+import { JustInDevMode } from "./components/JustInDevMode.js";
 import {
   NavigationHeader,
   NavigationHeaderHolder,
   SvgIcon,
 } from "./components/styled/index.js";
+import { useBackendContext } from "./context/backend.js";
 import { useTranslationContext } from "./context/translation.js";
-import settingsIcon from "./svg/settings_black_24dp.svg";
+import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
 import qrIcon from "./svg/qr_code_24px.svg";
+import settingsIcon from "./svg/settings_black_24dp.svg";
 import warningIcon from "./svg/warning_24px.svg";
-import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
-import { wxApi } from "./wxApi.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { JustInDevMode } from "./components/JustInDevMode.js";
 
 /**
  * List of pages used by the wallet
@@ -133,13 +133,8 @@ export const Pages = {
   ),
 };
 
-export function PopupNavBar({
-  path = "",
-}: {
-  path?: string;
-}): // api: typeof wxApi,
-VNode {
-  const api = wxApi; //FIXME: as parameter
+export function PopupNavBar({ path = "" }: { path?: string }): VNode {
+  const api = useBackendContext();
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(
       WalletApiOperation.GetUserAttentionUnreadCount,
@@ -194,7 +189,7 @@ VNode {
 export function WalletNavBar({ path = "" }: { path?: string }): VNode {
   const { i18n } = useTranslationContext();
 
-  const api = wxApi; //FIXME: as parameter
+  const api = useBackendContext();
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(
       WalletApiOperation.GetUserAttentionUnreadCount,
diff --git 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
index 5d5dae092..85b43fb4e 100644
--- a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
+++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
@@ -22,11 +22,11 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, JSX, VNode } from "preact";
 import { useEffect } from "preact/hooks";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Avatar } from "../mui/Avatar.js";
 import { Typography } from "../mui/Typography.js";
-import { wxApi } from "../wxApi.js";
 import Banner from "./Banner.js";
 import { Time } from "./Time.js";
 
@@ -35,12 +35,13 @@ interface Props extends JSX.HTMLAttributes {
 }
 
 export function PendingTransactions({ goToTransaction }: Props): VNode {
+  const api = useBackendContext();
   const state = useAsyncAsHook(() =>
-    wxApi.wallet.call(WalletApiOperation.GetTransactions, {}),
+    api.wallet.call(WalletApiOperation.GetTransactions, {}),
   );
 
   useEffect(() => {
-    return wxApi.listener.onUpdateNotification(
+    return api.listener.onUpdateNotification(
       [NotificationType.WithdrawGroupFinished],
       state?.retry,
     );
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
index 6461f76e3..47c10347c 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
@@ -25,11 +25,11 @@ import { Loading } from "../components/Loading.js";
 import { LoadingError } from "../components/LoadingError.js";
 import { Modal } from "../components/Modal.js";
 import { Time } from "../components/Time.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../mui/handlers.js";
 import { compose, StateViewMap } from "../utils/index.js";
-import { wxApi } from "../wxApi.js";
 import { Amount } from "./Amount.js";
 import { Link } from "./styled/index.js";
 
@@ -98,7 +98,8 @@ interface Props {
   proposalId: string;
 }
 
-function useComponentState({ proposalId }: Props, api: typeof wxApi): State {
+function useComponentState({ proposalId }: Props): State {
+  const api = useBackendContext();
   const [show, setShow] = useState(false);
   const hook = useAsyncAsHook(async () => {
     if (!show) return undefined;
@@ -139,7 +140,7 @@ const viewMapping: StateViewMap<State> = {
 
 export const ShowFullContractTermPopup = compose(
   "ShowFullContractTermPopup",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
 
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
index 4d2c4346e..5d5ad3ba2 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
@@ -18,7 +18,6 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ToggleHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { TermsState } from "./utils.js";
 import {
@@ -26,7 +25,7 @@ import {
   LoadingUriView,
   ShowButtonsAcceptedTosView,
   ShowButtonsNonAcceptedTosView,
-  ShowTosContentView,
+  ShowTosContentView
 } from "./views.js";
 
 export interface Props {
@@ -89,6 +88,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const TermsOfService = compose(
   "TermsOfService",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
index c5be71ef0..5006cefce 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
@@ -16,15 +16,15 @@
 
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 import { buildTermsOfServiceState } from "./utils.js";
 
 export function useComponentState(
   { exchangeUrl, onChange }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const readOnly = !onChange;
   const [showContent, setShowContent] = useState<boolean>(readOnly);
   const [errorAccepting, setErrorAccepting] = useState<Error | undefined>(
diff --git a/packages/taler-wallet-webextension/src/context/backend.ts 
b/packages/taler-wallet-webextension/src/context/backend.ts
new file mode 100644
index 000000000..3e9e1f0ab
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/context/backend.ts
@@ -0,0 +1,53 @@
+/*
+ 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 { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext } from "preact/hooks";
+import { wxApi, WxApiType } from "../wxApi.js";
+
+type Type = WxApiType
+
+const initial = wxApi;
+
+const Context = createContext<Type>(initial);
+
+type Props = Partial<WxApiType> & {
+  children: ComponentChildren;
+}
+
+export const BackendProvider = ({
+  wallet,
+  background,
+  listener,
+  children,
+}: Props): VNode => {
+
+  return h(Context.Provider, {
+    value: {
+      wallet: wallet ?? initial.wallet,
+      background: background ?? initial.background,
+      listener: listener ?? initial.listener
+    },
+    children,
+  });
+};
+
+export const useBackendContext = (): Type => useContext(Context);
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
index 539709821..9ff3ddd1d 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
@@ -19,7 +19,6 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { 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 } from "./views.js";
 
@@ -64,6 +63,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const DepositPage = compose(
   "Deposit",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
index 77e918ca9..fbcd107ef 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
@@ -16,14 +16,14 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerDepositUri, amountStr, cancel, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const info = useAsyncAsHook(async () => {
     if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
     if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
index a5bfed4a8..1c8d4708d 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
@@ -20,16 +20,18 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
-import { mountHook } from "../../test-utils.js";
 import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { Props } from "./index.js";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 describe("Deposit CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
-    const { handler, mock } = createWalletApiMock();
-    const props = {
+    const { handler, TestingContext } = createWalletApiMock();
+
+    const props: Props = {
       talerDepositUri: undefined,
       amountStr: undefined,
       cancel: async () => {
@@ -39,32 +41,28 @@ describe("Deposit CTA states", () => {
         null;
       },
     };
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
 
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equals("loading");
+      },
+      ({ status, error }) => {
+        expect(status).equals("loading-uri");
 
-      expect(status).equals("loading-uri");
+        if (!error) expect.fail();
+        if (!error.hasError) expect.fail();
+        if (error.operational) expect.fail();
+        expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
+      },
+    ], TestingContext)
 
-      if (!error) expect.fail();
-      if (!error.hasError) expect.fail();
-      if (error.operational) expect.fail();
-      expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
-    }
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be ready after loading", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
+
     handler.addWalletCallResponse(
       WalletApiOperation.PrepareDeposit,
       undefined,
@@ -73,6 +71,7 @@ describe("Deposit CTA states", () => {
         totalDepositCost: "EUR:1.2",
       },
     );
+
     const props = {
       talerDepositUri: "payto://refund/asdasdas",
       amountStr: "EUR:1",
@@ -84,28 +83,21 @@ describe("Deposit CTA states", () => {
       },
     };
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ready") expect.fail();
-      if (state.error) expect.fail();
-      expect(state.confirm.onClick).not.undefined;
-      expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
-      expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
-      expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equals("loading");
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        if (state.error) expect.fail();
+        expect(state.confirm.onClick).not.undefined;
+        expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
+        expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
+        expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
index 01dbb6d6d..0569e8e5f 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
@@ -22,7 +22,6 @@ import { ButtonHandler, TextFieldHandler } from 
"../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { ExchangeSelectionPage } from 
"../../wallet/ExchangeSelection/index.js";
 import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -78,6 +77,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const InvoiceCreatePage = compose(
   "InvoiceCreatePage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index 6007b5193..a26167f8e 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -23,17 +23,17 @@ import {
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 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";
 
 export function useComponentState(
   { amount: amountStr, onClose, onSuccess }: Props,
-  api: typeof wxApi,
 ): RecursiveState<State> {
   const amount = Amounts.parseOrThrow(amountStr);
+  const api = useBackendContext()
 
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListExchanges, {}),
@@ -158,8 +158,8 @@ export function useComponentState(
           subject === undefined
             ? undefined
             : !subject
-            ? "Can't be empty"
-            : undefined,
+              ? "Can't be empty"
+              : undefined,
         value: subject ?? "",
         onInput: async (e) => setSubject(e),
       },
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index 6e16b528c..78f244964 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -18,13 +18,12 @@ import {
   AbsoluteTime,
   AmountJson,
   PreparePayResult,
-  TalerErrorDetail,
+  TalerErrorDetail
 } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { 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 } from "./views.js";
 
@@ -92,6 +91,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const InvoicePayPage = compose(
   "InvoicePayPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index c7fb48958..eb50ba748 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -25,14 +25,14 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const hook = useAsyncAsHook(async () => {
     const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, 
{
       talerUri: talerPayPullUri,
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/index.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
index 9bca8f74f..45e4a5b88 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
@@ -19,13 +19,12 @@ import {
   PreparePayResult,
   PreparePayResultAlreadyConfirmed,
   PreparePayResultInsufficientBalance,
-  PreparePayResultPaymentPossible,
+  PreparePayResultPaymentPossible
 } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { BaseView, LoadingUriView } from "./views.js";
 
@@ -96,6 +95,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const PaymentPage = compose(
   "Payment",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
index 970af5b81..7690910e6 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
@@ -23,16 +23,16 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
   const [payErrMsg, setPayErrMsg] = useState<TalerError | 
undefined>(undefined);
+  const api = useBackendContext()
 
   const hook = useAsyncAsHook(async () => {
     if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
index b02ac6274..aba76fcf4 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
@@ -30,54 +30,46 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
+import { tests } from "../../../../web-util/src/index.browser.js";
 import { mountHook, nullFunction } from "../../test-utils.js";
 import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 describe("Payment CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: undefined,
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
 
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("loading-uri");
-      if (error === undefined) expect.fail();
-      expect(error.hasError).true;
-      expect(error.operational).false;
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      ({ status, error }) => {
+        expect(status).equals("loading-uri");
+        if (error === undefined) expect.fail();
+        expect(error.hasError).true;
+        expect(error.operational).false;
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 
   it("should response with no balance", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
 
     handler.addWalletCallResponse(
@@ -94,41 +86,34 @@ describe("Payment CTA states", () => {
       { balances: [] },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "no-balance-for-currency") {
-        expect(r).eq({});
-        return;
-      }
-      expect(r.balance).undefined;
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "no-balance-for-currency") {
+          expect(state).eq({});
+          return;
+        }
+        expect(state.balance).undefined;
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should not be able to pay if there is no enough balance", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.PreparePayForUri,
       undefined,
@@ -153,38 +138,31 @@ describe("Payment CTA states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "no-enough-balance") expect.fail();
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "no-enough-balance") expect.fail();
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be able to pay (without fee)", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.PreparePayForUri,
       undefined,
@@ -209,42 +187,36 @@ describe("Payment CTA states", () => {
         ],
       },
     );
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") {
-        expect(r).eq({});
-        return;
-      }
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
-      expect(r.payHandler.onClick).not.undefined;
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") {
+          expect(state).eq({});
+          return;
+        }
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
+        expect(state.payHandler.onClick).not.undefined;
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 
   it("should be able to pay (with fee)", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.PreparePayForUri,
       undefined,
@@ -269,39 +241,32 @@ describe("Payment CTA states", () => {
         ],
       },
     );
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      expect(r.payHandler.onClick).not.undefined;
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        expect(state.payHandler.onClick).not.undefined;
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should get confirmation done after pay successfully", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.PreparePayForUri,
       undefined,
@@ -332,35 +297,30 @@ describe("Payment CTA states", () => {
       contractTerms: {},
     } as ConfirmPayResult);
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") {
-        expect(r).eq({});
-        return;
-      }
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      if (r.payHandler.onClick === undefined) expect.fail();
-      r.payHandler.onClick();
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") {
+          expect(state).eq({});
+          return;
+        }
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        if (state.payHandler.onClick === undefined) expect.fail();
+        state.payHandler.onClick();
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 
   it("should not stay in ready state after pay with error", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
@@ -397,62 +357,50 @@ describe("Payment CTA states", () => {
       lastError: { code: 1 },
     } as ConfirmPayResult);
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
-      if (r.payHandler.onClick === undefined) expect.fail();
-      r.payHandler.onClick();
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
-      expect(r.payHandler.onClick).undefined;
-      if (r.payHandler.error === undefined) expect.fail();
-      //FIXME: error message here is bad
-      expect(r.payHandler.error.errorDetail.hint).eq(
-        "could not confirm payment",
-      );
-      expect(r.payHandler.error.errorDetail.payResult).deep.equal({
-        type: ConfirmPayResultType.Pending,
-        lastError: { code: 1 },
-      });
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
+        if (state.payHandler.onClick === undefined) expect.fail();
+        state.payHandler.onClick();
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
+        expect(state.payHandler.onClick).undefined;
+        if (state.payHandler.error === undefined) expect.fail();
+        //FIXME: error message here is bad
+        expect(state.payHandler.error.errorDetail.hint).eq(
+          "could not confirm payment",
+        );
+        expect(state.payHandler.error.errorDetail.payResult).deep.equal({
+          type: ConfirmPayResultType.Pending,
+          lastError: { code: 1 },
+        });
+      },
+    ], TestingContext)
 
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 
   it("should update balance if a coins is withdraw", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
     const props = {
       talerPayUri: "taller://pay",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
-      onSuccess: async () => {
-        null;
-      },
+      onSuccess: nullFunction,
     };
 
     handler.addWalletCallResponse(
@@ -507,46 +455,30 @@ describe("Payment CTA states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") {
-        expect(r).eq({});
-        return;
-      }
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
-      expect(r.payHandler.onClick).not.undefined;
-
-      handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") {
-        expect(r).eq({});
-        return;
-      }
-      expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
-      expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
-      // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
-      expect(r.payHandler.onClick).not.undefined;
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail()
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
+        expect(state.payHandler.onClick).not.undefined;
+
+        handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail()
+        expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
+        expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
+        // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
+        expect(state.payHandler.onClick).not.undefined;
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/index.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
index 4a65c571b..4a6fc79c9 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
@@ -18,7 +18,6 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { 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 } from "./views.js";
 
@@ -60,6 +59,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const RecoveryPage = compose(
   "Recovery",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
index 3a5d94e2e..018d61c03 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
@@ -16,13 +16,13 @@
 
 import { parseRecoveryUri } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { wxApi } from "../../wxApi.js";
+import { useBackendContext } from "../../context/backend.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerRecoveryUri, onCancel, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   if (!talerRecoveryUri) {
     return {
       status: "loading-uri",
@@ -48,7 +48,7 @@ export function useComponentState(
   const recovery = info;
 
   async function recoverBackup(): Promise<void> {
-    await wxApi.wallet.call(WalletApiOperation.ImportBackupRecovery, {
+    await api.wallet.call(WalletApiOperation.ImportBackupRecovery, {
       recovery,
     });
     onSuccess();
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/index.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
index 099f72919..158f5c179 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
@@ -19,13 +19,12 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import {
   IgnoredView,
   InProgressView,
   LoadingUriView,
-  ReadyView,
+  ReadyView
 } from "./views.js";
 
 export interface Props {
@@ -90,6 +89,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const RefundPage = compose(
   "Refund",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/state.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
index 94c5567d6..624ab2fb2 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
@@ -17,14 +17,14 @@
 import { Amounts, NotificationType } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerRefundUri, cancel, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const [ignored, setIgnored] = useState(false);
 
   const info = useAsyncAsHook(async () => {
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/test.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
index 927c45981..5fbf3743e 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
@@ -20,75 +20,50 @@
  */
 
 import {
-  AmountJson,
   Amounts,
   NotificationType,
-  OrderShortInfo,
-  PrepareRefundResult,
+  OrderShortInfo
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
-import { mountHook } from "../../test-utils.js";
-import { createWalletApiMock } from "../../test-utils.js";
+import { tests } from "../../../../web-util/src/index.browser.js";
+import { createWalletApiMock, mountHook, nullFunction } from 
"../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 describe("Refund CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          {
-            talerRefundUri: undefined,
-            cancel: async () => {
-              null;
-            },
-            onSuccess: async () => {
-              null;
-            },
-          },
-          mock,
-          // {
-          //   prepareRefund: async () => ({}),
-          //   applyRefund: async () => ({}),
-          //   onUpdateNotification: async () => ({}),
-          // } as any,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
+    const props = {
+      talerRefundUri: undefined,
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     }
 
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("loading-uri");
-      if (!error) expect.fail();
-      if (!error.hasError) expect.fail();
-      if (error.operational) expect.fail();
-      expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      ({ status, error }) => {
+        expect(status).equals("loading-uri");
+        if (!error) expect.fail();
+        if (!error.hasError) expect.fail();
+        if (error.operational) expect.fail();
+        expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be ready after loading", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerRefundUri: "taler://refund/asdasdas",
-      cancel: async () => {
-        null;
-      },
-      onSuccess: async () => {
-        null;
-      },
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     };
 
     handler.addWalletCallResponse(WalletApiOperation.PrepareRefund, undefined, 
{
@@ -108,61 +83,28 @@ describe("Refund CTA states", () => {
       } as OrderShortInfo,
     });
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          props,
-          mock,
-          //   {
-          //     prepareRefund: async () =>
-          //     ({
-          //       effectivePaid: "EUR:2",
-          //       awaiting: "EUR:2",
-          //       gone: "EUR:0",
-          //       granted: "EUR:0",
-          //       pending: false,
-          //       proposalId: "1",
-          //       info: {
-          //         contractTermsHash: "123",
-          //         merchant: {
-          //           name: "the merchant name",
-          //         },
-          //         orderId: "orderId1",
-          //         summary: "the summary",
-          //       },
-          //     } as PrepareRefundResult as any),
-          //     applyRefund: async () => ({}),
-          //     onUpdateNotification: async () => ({}),
-          //   } as any,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ready") expect.fail();
-      if (state.error) expect.fail();
-      expect(state.accept.onClick).not.undefined;
-      expect(state.ignore.onClick).not.undefined;
-      expect(state.merchantName).eq("the merchant name");
-      expect(state.orderId).eq("orderId1");
-      expect(state.products).undefined;
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        if (state.error) expect.fail();
+        expect(state.accept.onClick).not.undefined;
+        expect(state.ignore.onClick).not.undefined;
+        expect(state.merchantName).eq("the merchant name");
+        expect(state.orderId).eq("orderId1");
+        expect(state.products).undefined;
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be ignored after clicking the ignore button", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerRefundUri: "taler://refund/asdasdas",
       cancel: async () => {
@@ -189,102 +131,36 @@ describe("Refund CTA states", () => {
         summary: "the summary",
       } as OrderShortInfo,
     });
-    // handler.addWalletCall(WalletApiOperation.ApplyRefund)
-    // handler.addWalletCall(WalletApiOperation.PrepareRefund, undefined, {
-    //   awaiting: "EUR:1",
-    //   effectivePaid: "EUR:2",
-    //   gone: "EUR:0",
-    //   granted: "EUR:1",
-    //   pending: true,
-    //   proposalId: "1",
-    //   info: {
-    //     contractTermsHash: "123",
-    //     merchant: {
-    //       name: "the merchant name",
-    //     },
-    //     orderId: "orderId1",
-    //     summary: "the summary",
-    //   } as OrderShortInfo,
-    // })
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          props,
-          mock,
-          // {
-          //   prepareRefund: async () =>
-          //   ({
-          //     effectivePaid: "EUR:2",
-          //     awaiting: "EUR:2",
-          //     gone: "EUR:0",
-          //     granted: "EUR:0",
-          //     pending: false,
-          //     proposalId: "1",
-          //     info: {
-          //       contractTermsHash: "123",
-          //       merchant: {
-          //         name: "the merchant name",
-          //       },
-          //       orderId: "orderId1",
-          //       summary: "the summary",
-          //     },
-          //   } as PrepareRefundResult as any),
-          //   applyRefund: async () => ({}),
-          //   onUpdateNotification: async () => ({}),
-          // } as any,
-        ),
-      );
 
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ready") {
-        expect(state).eq({});
-        return;
-      }
-      if (state.error) {
-        expect(state).eq({});
-        return;
-      }
-      expect(state.accept.onClick).not.undefined;
-      expect(state.merchantName).eq("the merchant name");
-      expect(state.orderId).eq("orderId1");
-      expect(state.products).undefined;
-
-      if (state.ignore.onClick === undefined) expect.fail();
-      state.ignore.onClick();
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ignored") {
-        expect(state).eq({});
-        return;
-      }
-      if (state.error) {
-        expect(state).eq({});
-        return;
-      }
-      expect(state.merchantName).eq("the merchant name");
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail()
+        if (state.error) expect.fail()
+        expect(state.accept.onClick).not.undefined;
+        expect(state.merchantName).eq("the merchant name");
+        expect(state.orderId).eq("orderId1");
+        expect(state.products).undefined;
+
+        if (state.ignore.onClick === undefined) expect.fail();
+        state.ignore.onClick();
+      },
+      (state) => {
+        if (state.status !== "ignored") expect.fail()
+        if (state.error) expect.fail()
+        expect(state.merchantName).eq("the merchant name");
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be in progress when doing refresh", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerRefundUri: "taler://refund/asdasdas",
       cancel: async () => {
@@ -344,67 +220,42 @@ describe("Refund CTA states", () => {
       } as OrderShortInfo,
     });
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "in-progress") {
-        expect(state).eq({});
-        return;
-      }
-      if (state.error) expect.fail();
-      expect(state.merchantName).eq("the merchant name");
-      expect(state.products).undefined;
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
-      // expect(state.progress).closeTo(1 / 3, 0.01)
-
-      handler.notifyEventFromWallet(NotificationType.RefreshMelted);
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "in-progress") {
-        expect(state).eq({});
-        return;
-      }
-      if (state.error) expect.fail();
-      expect(state.merchantName).eq("the merchant name");
-      expect(state.products).undefined;
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
-      // expect(state.progress).closeTo(2 / 3, 0.01)
-
-      handler.notifyEventFromWallet(NotificationType.RefreshMelted);
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "in-progress") expect.fail()
+        if (state.error) expect.fail();
+        expect(state.merchantName).eq("the merchant name");
+        expect(state.products).undefined;
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
+        // expect(state.progress).closeTo(1 / 3, 0.01)
+
+        handler.notifyEventFromWallet(NotificationType.RefreshMelted);
+      },
+      (state) => {
+        if (state.status !== "in-progress") expect.fail()
+        if (state.error) expect.fail();
+        expect(state.merchantName).eq("the merchant name");
+        expect(state.products).undefined;
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
+        // expect(state.progress).closeTo(2 / 3, 0.01)
+
+        handler.notifyEventFromWallet(NotificationType.RefreshMelted);
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail()
+        if (state.error) expect.fail();
+        expect(state.merchantName).eq("the merchant name");
+        expect(state.products).undefined;
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
 
-      if (state.status !== "ready") {
-        expect(state).eq({});
-        return;
-      }
-      if (state.error) expect.fail();
-      expect(state.merchantName).eq("the merchant name");
-      expect(state.products).undefined;
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
-    }
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/index.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/index.ts
index ff917008f..a29a3eadb 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/index.ts
@@ -19,13 +19,12 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import {
   AcceptedView,
   IgnoredView,
   LoadingUriView,
-  ReadyView,
+  ReadyView
 } from "./views.js";
 
 export interface Props {
@@ -84,6 +83,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const TipPage = compose(
   "Tip",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/state.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
index ea9ba1b37..0ca213b01 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
@@ -16,14 +16,14 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerTipUri, onCancel, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const tipInfo = useAsyncAsHook(async () => {
     if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
     const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/test.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
index e57b9ec4d..21ed95218 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
@@ -22,54 +22,42 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
-import { mountHook } from "../../test-utils.js";
+import { tests } from "../../../../web-util/src/index.browser.js";
+import { mountHook, nullFunction } from "../../test-utils.js";
 import { createWalletApiMock } from "../../test-utils.js";
+import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
 
 describe("Tip CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          {
-            talerTipUri: undefined,
-            onCancel: async () => {
-              null;
-            },
-            onSuccess: async () => {
-              null;
-            },
-          },
-          mock,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
+    const props: Props = {
+      talerTipUri: undefined,
+      onCancel: nullFunction,
+      onSuccess: nullFunction,
     }
 
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("loading-uri");
-      if (!error) expect.fail();
-      if (!error.hasError) expect.fail();
-      if (error.operational) expect.fail();
-      expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      ({ status, error }) => {
+        expect(status).equals("loading-uri");
+        if (!error) expect.fail();
+        if (!error.hasError) expect.fail();
+        if (error.operational) expect.fail();
+        expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be ready for accepting the tip", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
     handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
       accepted: false,
@@ -83,78 +71,59 @@ describe("Tip CTA states", () => {
       tipAmountRaw: "",
     });
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          {
-            talerTipUri: "taler://tip/asd",
-            onCancel: async () => {
-              null;
-            },
-            onSuccess: async () => {
-              null;
-            },
-          },
-          mock,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
+    const props: Props = {
+      talerTipUri: "taler://tip/asd",
+      onCancel: nullFunction,
+      onSuccess: nullFunction,
     }
 
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ready") {
-        expect(state).eq({ status: "ready" });
-        return;
-      }
-      if (state.error) expect.fail();
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
-      expect(state.merchantBaseUrl).eq("merchant url");
-      expect(state.exchangeBaseUrl).eq("exchange url");
-      if (state.accept.onClick === undefined) expect.fail();
-
-      handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
-      state.accept.onClick();
-    }
-
-    handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
-      accepted: true,
-      exchangeBaseUrl: "exchange url",
-      merchantBaseUrl: "merchant url",
-      tipAmountEffective: "EUR:1",
-      walletTipId: "tip_id",
-      expirationTimestamp: {
-        t_s: 1,
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
       },
-      tipAmountRaw: "",
-    });
-    expect(await waitForStateUpdate()).true;
+      (state) => {
+        if (state.status !== "ready") {
+          expect(state).eq({ status: "ready" });
+          return;
+        }
+        if (state.error) expect.fail();
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
+        expect(state.merchantBaseUrl).eq("merchant url");
+        expect(state.exchangeBaseUrl).eq("exchange url");
+        if (state.accept.onClick === undefined) expect.fail();
+
+        handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
+        state.accept.onClick();
+
+        handler.addWalletCallResponse(WalletApiOperation.PrepareTip, 
undefined, {
+          accepted: true,
+          exchangeBaseUrl: "exchange url",
+          merchantBaseUrl: "merchant url",
+          tipAmountEffective: "EUR:1",
+          walletTipId: "tip_id",
+          expirationTimestamp: {
+            t_s: 1,
+          },
+          tipAmountRaw: "",
+        });
 
-    {
-      const state = pullLastResultOrThrow();
+      },
+      (state) => {
+        if (state.status !== "accepted") expect.fail()
+        if (state.error) expect.fail();
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
+        expect(state.merchantBaseUrl).eq("merchant url");
+        expect(state.exchangeBaseUrl).eq("exchange url");
+      },
+    ], TestingContext)
 
-      if (state.status !== "accepted") {
-        expect(state).eq({ status: "accepted" });
-        return;
-      }
-      if (state.error) expect.fail();
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
-      expect(state.merchantBaseUrl).eq("merchant url");
-      expect(state.exchangeBaseUrl).eq("exchange url");
-    }
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
-  it("should be ignored after clicking the ignore button", async () => {
-    const { handler, mock } = createWalletApiMock();
+  it.skip("should be ignored after clicking the ignore button", async () => {
+    const { handler, TestingContext } = createWalletApiMock();
     handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
       exchangeBaseUrl: "exchange url",
       merchantBaseUrl: "merchant url",
@@ -167,46 +136,34 @@ describe("Tip CTA states", () => {
       tipAmountRaw: "",
     });
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          {
-            talerTipUri: "taler://tip/asd",
-            onCancel: async () => {
-              null;
-            },
-            onSuccess: async () => {
-              null;
-            },
-          },
-          mock,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
+    const props: Props = {
+      talerTipUri: "taler://tip/asd",
+      onCancel: nullFunction,
+      onSuccess: nullFunction,
     }
 
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-
-      if (state.status !== "ready") expect.fail();
-      if (state.error) expect.fail();
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
-      expect(state.merchantBaseUrl).eq("merchant url");
-      expect(state.exchangeBaseUrl).eq("exchange url");
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        if (state.error) expect.fail();
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
+        expect(state.merchantBaseUrl).eq("merchant url");
+        expect(state.exchangeBaseUrl).eq("exchange url");
+
+        //FIXME: add ignore button
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should render accepted if the tip has been used previously", async () => 
{
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
     handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
       accepted: true,
@@ -220,40 +177,28 @@ describe("Tip CTA states", () => {
       tipAmountRaw: "",
     });
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() =>
-        useComponentState(
-          {
-            talerTipUri: "taler://tip/asd",
-            onCancel: async () => {
-              null;
-            },
-            onSuccess: async () => {
-              null;
-            },
-          },
-          mock,
-        ),
-      );
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
+    const props: Props = {
+      talerTipUri: "taler://tip/asd",
+      onCancel: nullFunction,
+      onSuccess: nullFunction,
     }
 
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status, error }) => {
+        expect(status).equals("loading");
+        expect(error).undefined;
+      },
+      (state) => {
+        if (state.status !== "accepted") expect.fail();
+        if (state.error) expect.fail();
+        expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
+        expect(state.merchantBaseUrl).eq("merchant url");
+        expect(state.exchangeBaseUrl).eq("exchange url");
+      },
+    ], TestingContext)
 
-      if (state.status !== "accepted") expect.fail();
-      if (state.error) expect.fail();
-      expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
-      expect(state.merchantBaseUrl).eq("merchant url");
-      expect(state.exchangeBaseUrl).eq("exchange url");
-    }
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 });
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
index 8d51ff3e0..0715bb60e 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
@@ -19,7 +19,6 @@ import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -66,6 +65,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const TransferCreatePage = compose(
   "TransferCreatePage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index c5e143f42..3536014da 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -17,19 +17,19 @@
 import {
   Amounts,
   TalerErrorDetail,
-  TalerProtocolTimestamp,
+  TalerProtocolTimestamp
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { format, isFuture, parse } from "date-fns";
+import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { amount: amountStr, onClose, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const amount = Amounts.parseOrThrow(amountStr);
 
   const [subject, setSubject] = useState<string | undefined>();
@@ -124,8 +124,8 @@ export function useComponentState(
         subject === undefined
           ? undefined
           : !subject
-          ? "Can't be empty"
-          : undefined,
+            ? "Can't be empty"
+            : undefined,
       value: subject ?? "",
       onInput: async (e) => setSubject(e),
     },
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
index 399f1e290..de6ad3b79 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
@@ -17,13 +17,12 @@
 import {
   AbsoluteTime,
   AmountJson,
-  TalerErrorDetail,
+  TalerErrorDetail
 } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { 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 } from "./views.js";
 
@@ -69,6 +68,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const TransferPickupPage = compose(
   "TransferPickupPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
index e8fb99ab7..45bec28fb 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
@@ -18,18 +18,18 @@ import {
   AbsoluteTime,
   Amounts,
   TalerErrorDetail,
-  TalerProtocolTimestamp,
+  TalerProtocolTimestamp
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { talerPayPushUri, onClose, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
       talerUri: talerPayPushUri,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index 68b314c07..9e5943161 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -20,10 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { State as SelectExchangeState } from 
"../../hooks/useSelectedExchange.js";
 import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import {
   useComponentStateFromParams,
-  useComponentStateFromURI,
+  useComponentStateFromURI
 } from "./state.js";
 
 import { ExchangeSelectionPage } from 
"../../wallet/ExchangeSelection/index.js";
@@ -96,11 +95,11 @@ const viewMapping: StateViewMap<State> = {
 
 export const WithdrawPageFromURI = compose(
   "WithdrawPageFromURI",
-  (p: PropsFromURI) => useComponentStateFromURI(p, wxApi),
+  (p: PropsFromURI) => useComponentStateFromURI(p),
   viewMapping,
 );
 export const WithdrawPageFromParams = compose(
   "WithdrawPageFromParams",
-  (p: PropsFromParams) => useComponentStateFromParams(p, wxApi),
+  (p: PropsFromParams) => useComponentStateFromParams(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 9bb29fbd6..4420221fc 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -19,20 +19,20 @@ import {
   AmountJson,
   Amounts,
   ExchangeListItem,
-  ExchangeTosStatus,
+  ExchangeTosStatus
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 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";
 
 export function useComponentStateFromParams(
   { amount, cancel, onSuccess }: PropsFromParams,
-  api: typeof wxApi,
 ): RecursiveState<State> {
+  const api = useBackendContext()
   const uriInfoHook = useAsyncAsHook(async () => {
     const exchanges = await api.wallet.call(
       WalletApiOperation.ListExchanges,
@@ -84,14 +84,13 @@ export function useComponentStateFromParams(
       chosenAmount,
       exchangeList,
       undefined,
-      api,
     );
 }
 
 export function useComponentStateFromURI(
   { talerWithdrawUri, cancel, onSuccess }: PropsFromURI,
-  api: typeof wxApi,
 ): RecursiveState<State> {
+  const api = useBackendContext()
   /**
    * Ask the wallet about the withdraw URI
    */
@@ -158,7 +157,6 @@ export function useComponentStateFromURI(
       chosenAmount,
       exchangeList,
       defaultExchange,
-      api,
     );
 }
 
@@ -176,8 +174,8 @@ function exchangeSelectionState(
   chosenAmount: AmountJson,
   exchangeList: ExchangeListItem[],
   defaultExchange: string | undefined,
-  api: typeof wxApi,
 ): RecursiveState<State> {
+  const api = useBackendContext()
   const selectedExchange = useSelectedExchange({
     currency: chosenAmount.currency,
     defaultExchange,
@@ -278,10 +276,10 @@ function exchangeSelectionState(
     //TODO: calculate based on exchange info
     const ageRestriction = ageRestrictionEnabled
       ? {
-          list: ageRestrictionOptions,
-          value: String(ageRestricted),
-          onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
-        }
+        list: ageRestrictionOptions,
+        value: String(ageRestricted),
+        onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
+      }
       : undefined;
 
     return {
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index 7fd8188ce..084b4368c 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -27,6 +27,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
+import { tests } from "../../../../web-util/src/index.browser.js";
 import { mountHook } from "../../test-utils.js";
 import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentStateFromURI } from "./state.js";
@@ -61,53 +62,45 @@ const exchanges: ExchangeListItem[] = [
   } as Partial<ExchangeListItem> as ExchangeListItem,
 ];
 
+const nullFunction = async (): Promise<void> => {
+  null;
+}
+
 describe("Withdraw CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
+
     const props = {
       talerWithdrawUri: undefined,
-      cancel: async () => {
-        null;
-      },
-      onSuccess: async () => {
-        null;
-      },
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     };
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentStateFromURI(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
 
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      if (status != "uri-error") expect.fail();
-      if (!error) expect.fail();
-      if (!error.hasError) expect.fail();
-      if (error.operational) expect.fail();
-      expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
-    }
+    const hookBehavior = await 
tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
+      ({ status }) => {
+        expect(status).equals("loading");
+      },
+      ({ status, error }) => {
+        if (status != "uri-error") expect.fail();
+        if (!error) expect.fail();
+        if (!error.hasError) expect.fail();
+        if (error.operational) expect.fail();
+        expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    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, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerWithdrawUri: "taler-withdraw://",
-      cancel: async () => {
-        null;
-      },
-      onSuccess: async () => {
-        null;
-      },
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.GetWithdrawalDetailsForUri,
       undefined,
@@ -117,39 +110,28 @@ describe("Withdraw CTA states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentStateFromURI(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equals("loading", "1");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("no-exchange", "3");
-
-      expect(error).undefined;
-    }
+    const hookBehavior = await 
tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
+      ({ status }) => {
+        expect(status).equals("loading");
+      },
+      ({ status, error }) => {
+        expect(status).equals("no-exchange");
+        expect(error).undefined;
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should be able to withdraw if tos are ok", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerWithdrawUri: "taler-withdraw://",
-      cancel: async () => {
-        null;
-      },
-      onSuccess: async () => {
-        null;
-      },
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     };
+
     handler.addWalletCallResponse(
       WalletApiOperation.GetWithdrawalDetailsForUri,
       undefined,
@@ -171,54 +153,38 @@ describe("Withdraw CTA states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentStateFromURI(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("loading");
-
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-      expect(state.status).equals("success");
-      if (state.status !== "success") return;
+    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.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;
-    }
+        expect(state.doWithdrawal.onClick).not.undefined;
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should accept the tos before withdraw", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = {
       talerWithdrawUri: "taler-withdraw://",
-      cancel: async () => {
-        null;
-      },
-      onSuccess: async () => {
-        null;
-      },
+      cancel: nullFunction,
+      onSuccess: nullFunction,
     };
+
     const exchangeWithNewTos = exchanges.map((e) => ({
       ...e,
       tosStatus: ExchangeTosStatus.New,
@@ -255,57 +221,39 @@ describe("Withdraw CTA states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentStateFromURI(props, mock));
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-      expect(status).equals("loading");
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status, error } = pullLastResultOrThrow();
-
-      expect(status).equals("loading");
-
-      expect(error).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const state = pullLastResultOrThrow();
-      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).undefined;
+    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;
 
-      // updateAcceptedVersionToCurrentVersion();
-      state.onTosUpdate();
-    }
+        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(await waitForStateUpdate()).true;
+        expect(state.doWithdrawal.onClick).undefined;
 
-    {
-      const state = pullLastResultOrThrow();
-      expect(state.status).equals("success");
-      if (state.status !== "success") return;
+        state.onTosUpdate();
+      },
+      (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.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;
-    }
+        expect(state.doWithdrawal.onClick).not.undefined;
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 });
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts 
b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
index 3361394a4..1a48cdca7 100644
--- a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
@@ -16,22 +16,23 @@
 
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../context/backend.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { platform } from "../platform/api.js";
-import { wxApi } from "../wxApi.js";
 
 export function useAutoOpenPermissions(): ToggleHandler {
+  const api = useBackendContext();
   const [enabled, setEnabled] = useState(false);
   const [error, setError] = useState<TalerError | undefined>();
   const toggle = async (): Promise<void> => {
-    return handleAutoOpenPerm(enabled, setEnabled).catch((e) => {
+    return handleAutoOpenPerm(enabled, setEnabled, api.background).catch((e) 
=> {
       setError(TalerError.fromException(e));
     });
   };
 
   useEffect(() => {
     async function getValue(): Promise<void> {
-      const res = await wxApi.background.containsHeaderListener();
+      const res = await api.background.containsHeaderListener();
       setEnabled(res.newValue);
     }
     getValue();
@@ -48,6 +49,7 @@ export function useAutoOpenPermissions(): ToggleHandler {
 async function handleAutoOpenPerm(
   isEnabled: boolean,
   onChange: (value: boolean) => void,
+  background: ReturnType<typeof useBackendContext>["background"],
 ): Promise<void> {
   if (!isEnabled) {
     // We set permissions here, since apparently FF wants this to be done
@@ -59,11 +61,11 @@ async function handleAutoOpenPerm(
       onChange(false);
       throw lastError;
     }
-    const res = await wxApi.background.toggleHeaderListener(granted);
+    const res = await background.toggleHeaderListener(granted);
     onChange(res.newValue);
   } else {
     try {
-      await wxApi.background
+      await background
         .toggleHeaderListener(false)
         .then((r) => onChange(r.newValue));
     } catch (e) {
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useBackupDeviceName.ts 
b/packages/taler-wallet-webextension/src/hooks/useBackupDeviceName.ts
index be81b7d7d..585a643a9 100644
--- a/packages/taler-wallet-webextension/src/hooks/useBackupDeviceName.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useBackupDeviceName.ts
@@ -16,7 +16,7 @@
 
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
-import { wxApi } from "../wxApi.js";
+import { useBackendContext } from "../context/backend.js";
 
 export interface BackupDeviceName {
   name: string;
@@ -28,17 +28,18 @@ export function useBackupDeviceName(): BackupDeviceName {
     name: "",
     update: () => Promise.resolve(),
   });
+  const api = useBackendContext()
 
   useEffect(() => {
     async function run(): Promise<void> {
       //create a first list of backup info by currency
-      const status = await wxApi.wallet.call(
+      const status = await api.wallet.call(
         WalletApiOperation.GetBackupInfo,
         {},
       );
 
       async function update(newName: string): Promise<void> {
-        await wxApi.wallet.call(WalletApiOperation.SetWalletDeviceId, {
+        await api.wallet.call(WalletApiOperation.SetWalletDeviceId, {
           walletDeviceId: newName,
         });
         setStatus((old) => ({ ...old, name: newName }));
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts 
b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
index 9b7d46ba7..649def599 100644
--- a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
@@ -16,22 +16,24 @@
 
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../context/backend.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { platform } from "../platform/api.js";
-import { wxApi } from "../wxApi.js";
 
 export function useClipboardPermissions(): ToggleHandler {
   const [enabled, setEnabled] = useState(false);
   const [error, setError] = useState<TalerError | undefined>();
+  const api = useBackendContext()
+
   const toggle = async (): Promise<void> => {
-    return handleClipboardPerm(enabled, setEnabled).catch((e) => {
+    return handleClipboardPerm(enabled, setEnabled, api.background).catch((e) 
=> {
       setError(TalerError.fromException(e));
     });
   };
 
   useEffect(() => {
     async function getValue(): Promise<void> {
-      const res = await wxApi.background.containsHeaderListener();
+      const res = await api.background.containsHeaderListener();
       setEnabled(res.newValue);
     }
     getValue();
@@ -49,6 +51,7 @@ export function useClipboardPermissions(): ToggleHandler {
 async function handleClipboardPerm(
   isEnabled: boolean,
   onChange: (value: boolean) => void,
+  background: ReturnType<typeof useBackendContext>["background"],
 ): Promise<void> {
   if (!isEnabled) {
     // We set permissions here, since apparently FF wants this to be done
@@ -62,11 +65,10 @@ async function handleClipboardPerm(
       onChange(false);
       throw lastError;
     }
-    // const res = await wxApi.toggleHeaderListener(granted);
     onChange(granted);
   } else {
     try {
-      await wxApi.background
+      await background
         .toggleHeaderListener(false)
         .then((r) => onChange(r.newValue));
     } catch (e) {
diff --git a/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts 
b/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
index a8564fddb..1f36ca6c0 100644
--- a/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
@@ -16,10 +16,11 @@
 
 import { WalletDiagnostics } from "@gnu-taler/taler-util";
 import { useEffect, useState } from "preact/hooks";
-import { wxApi } from "../wxApi.js";
+import { useBackendContext } from "../context/backend.js";
 
 export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
   const [timedOut, setTimedOut] = useState(false);
+  const api = useBackendContext();
   const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | 
undefined>(
     undefined,
   );
@@ -33,7 +34,7 @@ export function useDiagnostics(): [WalletDiagnostics | 
undefined, boolean] {
       }
     }, 1000);
     const doFetch = async (): Promise<void> => {
-      const d = await wxApi.background.getDiagnostics();
+      const d = await api.background.getDiagnostics();
       gotDiagnostics = true;
       setDiagnostics(d);
     };
diff --git a/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts 
b/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts
index e6473d1ec..ca2054931 100644
--- a/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts
@@ -16,7 +16,7 @@
 
 import { ProviderInfo, WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
-import { wxApi } from "../wxApi.js";
+import { useBackendContext } from "../context/backend.js";
 
 export interface ProviderStatus {
   info?: ProviderInfo;
@@ -26,11 +26,11 @@ export interface ProviderStatus {
 
 export function useProviderStatus(url: string): ProviderStatus | undefined {
   const [status, setStatus] = useState<ProviderStatus | undefined>(undefined);
-
+  const api = useBackendContext();
   useEffect(() => {
     async function run(): Promise<void> {
       //create a first list of backup info by currency
-      const status = await wxApi.wallet.call(
+      const status = await api.wallet.call(
         WalletApiOperation.GetBackupInfo,
         {},
       );
@@ -42,7 +42,7 @@ export function useProviderStatus(url: string): 
ProviderStatus | undefined {
 
       async function sync(): Promise<void> {
         if (info) {
-          await wxApi.wallet.call(WalletApiOperation.RunBackupCycle, {
+          await api.wallet.call(WalletApiOperation.RunBackupCycle, {
             providers: [info.syncProviderBaseUrl],
           });
         }
@@ -50,7 +50,7 @@ export function useProviderStatus(url: string): 
ProviderStatus | undefined {
 
       async function remove(): Promise<void> {
         if (info) {
-          await wxApi.wallet.call(WalletApiOperation.RemoveBackupProvider, {
+          await api.wallet.call(WalletApiOperation.RemoveBackupProvider, {
             provider: info.syncProviderBaseUrl,
           });
         }
diff --git a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts 
b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
index d5743eb2e..6ae55da61 100644
--- a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
@@ -15,22 +15,24 @@
  */
 
 import { useState, useEffect } from "preact/hooks";
-import { wxApi } from "../wxApi.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useBackendContext } from "../context/backend.js";
 
 export function useWalletDevMode(): ToggleHandler {
   const [enabled, setEnabled] = useState<undefined | boolean>(undefined);
   const [error, setError] = useState<TalerError | undefined>();
+  const api = useBackendContext();
+
   const toggle = async (): Promise<void> => {
-    return handleOpen(enabled, setEnabled).catch((e) => {
+    return handleOpen(enabled, setEnabled, api).catch((e) => {
       setError(TalerError.fromException(e));
     });
   };
 
   useEffect(() => {
     async function getValue(): Promise<void> {
-      const res = await wxApi.wallet.call(WalletApiOperation.GetVersion, {});
+      const res = await api.wallet.call(WalletApiOperation.GetVersion, {});
       setEnabled(res.devMode);
     }
     getValue();
@@ -47,9 +49,10 @@ export function useWalletDevMode(): ToggleHandler {
 async function handleOpen(
   currentValue: undefined | boolean,
   onChange: (value: boolean) => void,
+  api: ReturnType<typeof useBackendContext>,
 ): Promise<void> {
   const nextValue = !currentValue;
-  await wxApi.wallet.call(WalletApiOperation.SetDevMode, {
+  await api.wallet.call(WalletApiOperation.SetDevMode, {
     devModeEnabled: nextValue,
   });
   onChange(nextValue);
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 98c6d166c..8786b2ff7 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -22,13 +22,13 @@ import { BalanceTable } from 
"../components/BalanceTable.js";
 import { Loading } from "../components/Loading.js";
 import { LoadingError } from "../components/LoadingError.js";
 import { MultiActionButton } from "../components/MultiActionButton.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { ButtonHandler } from "../mui/handlers.js";
 import { compose, StateViewMap } from "../utils/index.js";
 import { AddNewActionView } from "../wallet/AddNewActionView.js";
-import { wxApi } from "../wxApi.js";
 import { NoBalanceHelp } from "./NoBalanceHelp.js";
 
 export interface Props {
@@ -67,10 +67,12 @@ export namespace State {
   }
 }
 
-function useComponentState(
-  { goToWalletDeposit, goToWalletHistory, goToWalletManualWithdraw }: Props,
-  api: typeof wxApi,
-): State {
+function useComponentState({
+  goToWalletDeposit,
+  goToWalletHistory,
+  goToWalletManualWithdraw,
+}: Props): State {
+  const api = useBackendContext();
   const [addingAction, setAddingAction] = useState(false);
   const state = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.GetBalances, {}),
@@ -128,7 +130,7 @@ const viewMapping: StateViewMap<State> = {
 
 export const BalancePage = compose(
   "BalancePage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
 
diff --git a/packages/taler-wallet-webextension/src/test-utils.ts 
b/packages/taler-wallet-webextension/src/test-utils.ts
index b4983b4c2..791773c42 100644
--- a/packages/taler-wallet-webextension/src/test-utils.ts
+++ b/packages/taler-wallet-webextension/src/test-utils.ts
@@ -31,6 +31,7 @@ import {
   VNode,
 } from "preact";
 import { render as renderToString } from "preact-render-to-string";
+import { BackendProvider } from "./context/backend.js";
 import { BackgroundApiClient, wxApi } from "./wxApi.js";
 
 // When doing tests we want the requestAnimationFrame to be as fast as 
possible.
@@ -252,7 +253,7 @@ type Subscriptions = {
 
 export function createWalletApiMock(): {
   handler: MockHandler;
-  mock: typeof wxApi;
+  TestingContext: FunctionalComponent<{ children: ComponentChildren }>
 } {
   const calls = new Array<CallRecord>();
   const subscriptions: Subscriptions = {};
@@ -357,5 +358,14 @@ export function createWalletApiMock(): {
     },
   };
 
-  return { handler, mock };
+  function TestingContext({ children }: { children: ComponentChildren }): 
VNode {
+    return create(BackendProvider, {
+      wallet: mock.wallet,
+      background: mock.background,
+      listener: mock.listener,
+      children,
+    }, children)
+  }
+
+  return { handler, TestingContext };
 }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
index 3205588af..2adcc9f74 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
@@ -15,9 +15,7 @@
  */
 
 import {
-  AmountJson,
-  BackupBackupProviderTerms,
-  TalerErrorDetail,
+  TalerErrorDetail
 } from "@gnu-taler/taler-util";
 import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
 import { Loading } from "../../components/Loading.js";
@@ -25,15 +23,13 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
 import {
   ButtonHandler,
   TextFieldHandler,
-  ToggleHandler,
+  ToggleHandler
 } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import {
-  LoadingUriView,
-  SelectProviderView,
-  ConfirmProviderView,
+  ConfirmProviderView, LoadingUriView,
+  SelectProviderView
 } from "./views.js";
 
 export interface Props {
@@ -90,6 +86,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const AddBackupProviderPage = compose(
   "AddBackupProvider",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
index 504ee4678..271a1bf98 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
@@ -17,15 +17,15 @@
 import {
   canonicalizeBaseUrl,
   Codec,
-  TalerErrorDetail,
+  TalerErrorDetail
 } from "@gnu-taler/taler-util";
 import {
   codecForSyncTermsOfServiceResponse,
-  WalletApiOperation,
+  WalletApiOperation
 } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { assertUnreachable } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 type UrlState<T> = UrlOk<T> | UrlError;
@@ -106,37 +106,37 @@ function useUrlState<T>(
     constHref == undefined
       ? undefined
       : async () => {
-          const req = await fetch(constHref).catch((e) => {
-            return setState({
-              status: "network-error",
-              href: constHref,
-            });
+        const req = await fetch(constHref).catch((e) => {
+          return setState({
+            status: "network-error",
+            href: constHref,
           });
-          if (!req) return;
+        });
+        if (!req) return;
 
-          if (req.status >= 400 && req.status < 500) {
-            setState({
-              status: "client-error",
-              code: req.status,
-            });
-            return;
-          }
-          if (req.status > 500) {
-            setState({
-              status: "server-error",
-              code: req.status,
-            });
-            return;
-          }
+        if (req.status >= 400 && req.status < 500) {
+          setState({
+            status: "client-error",
+            code: req.status,
+          });
+          return;
+        }
+        if (req.status > 500) {
+          setState({
+            status: "server-error",
+            code: req.status,
+          });
+          return;
+        }
 
-          const json = await req.json();
-          try {
-            const result = codec.decode(json);
-            setState({ status: "ok", result });
-          } catch (e: any) {
-            setState({ status: "parsing-error", json });
-          }
-        },
+        const json = await req.json();
+        try {
+          const result = codec.decode(json);
+          setState({ status: "ok", result });
+        } catch (e: any) {
+          setState({ status: "parsing-error", json });
+        }
+      },
     [host, path],
   );
 
@@ -145,8 +145,8 @@ function useUrlState<T>(
 
 export function useComponentState(
   { currency, onBack, onComplete, onPaymentRequired }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const [url, setHost] = useState<string | undefined>();
   const [name, setName] = useState<string | undefined>();
   const [tos, setTos] = useState(false);
@@ -223,8 +223,8 @@ export function useComponentState(
         !urlState || urlState.status !== "ok" || !name
           ? undefined
           : async () => {
-              setShowConfirm(true);
-            },
+            setShowConfirm(true);
+          },
     },
     urlOk: urlState?.status === "ok",
     url: {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
index 1143853f8..929e051cb 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
@@ -19,12 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
+import { tests } from "../../../../web-util/src/index.browser.js";
 import {
-  createWalletApiMock,
-  mountHook,
-  nullFunction,
+  createWalletApiMock, nullFunction
 } from "../../test-utils.js";
 import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
@@ -36,44 +34,21 @@ const props: Props = {
   onPaymentRequired: nullFunction,
 };
 describe("AddBackupProvider states", () => {
-  it("should start in 'select-provider' state", async () => {
-    const { handler, mock } = createWalletApiMock();
-
-    // handler.addWalletCallResponse(
-    //   WalletApiOperation.ListKnownBankAccounts,
-    //   undefined,
-    //   {
-    //     accounts: [],
-    //   },
-    // );
-
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const state = pullLastResultOrThrow();
-      expect(state.status).equal("select-provider");
-      if (state.status !== "select-provider") return;
-      expect(state.name.value).eq("");
-      expect(state.url.value).eq("");
-    }
 
-    //FIXME: this should not make an extra update
-    /**
-     * this may be due to useUrlState because is using an effect over
-     * a dependency with a timeout
-     */
-    // NOTE: do not remove this comment, keeping as an example
-    // await waitForStateUpdate()
-    // {
-    //   const state = pullLastResultOrThrow();
-    //   expect(state.status).equal("select-provider");
-    //   if (state.status !== "select-provider") return;
-    //   expect(state.name.value).eq("")
-    //   expect(state.url.value).eq("")
-    // }
-
-    await assertNoPendingUpdate();
+  it("should start in 'select-provider' state", async () => {
+    const { handler, TestingContext } = createWalletApiMock();
+
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      (state) => {
+        expect(state.status).equal("select-provider");
+        if (state.status !== "select-provider") return;
+        expect(state.name.value).eq("");
+        expect(state.url.value).eq("");
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index c9dbfb64d..6e987f965 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -42,11 +42,11 @@ import {
   SmallText,
   WarningBox,
 } from "../components/styled/index.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { Pages } from "../NavigationBar.js";
-import { wxApi } from "../wxApi.js";
 
 interface Props {
   onAddProvider: () => Promise<void>;
@@ -107,8 +107,9 @@ export function ShowRecoveryInfo({
 
 export function BackupPage({ onAddProvider }: Props): VNode {
   const { i18n } = useTranslationContext();
+  const api = useBackendContext();
   const status = useAsyncAsHook(() =>
-    wxApi.wallet.call(WalletApiOperation.GetBackupInfo, {}),
+    api.wallet.call(WalletApiOperation.GetBackupInfo, {}),
   );
   const [recoveryInfo, setRecoveryInfo] = useState<string>("");
   if (!status) {
@@ -124,7 +125,7 @@ export function BackupPage({ onAddProvider }: Props): VNode 
{
   }
 
   async function getRecoveryInfo(): Promise<void> {
-    const r = await wxApi.wallet.call(
+    const r = await api.wallet.call(
       WalletApiOperation.ExportBackupRecovery,
       {},
     );
@@ -158,7 +159,7 @@ export function BackupPage({ onAddProvider }: Props): VNode 
{
       providers={providers}
       onAddProvider={onAddProvider}
       onSyncAll={async () =>
-        wxApi.wallet.call(WalletApiOperation.RunBackupCycle, {}).then()
+        api.wallet.call(WalletApiOperation.RunBackupCycle, {}).then()
       }
       onShowInfo={getRecoveryInfo}
     />
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index 3f23515b2..ad4c759bf 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -20,11 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
 import {
   AmountFieldHandler,
   ButtonHandler,
-  SelectFieldHandler,
-  TextFieldHandler,
+  SelectFieldHandler
 } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { ManageAccountPage } from "../ManageAccount/index.js";
 import { useComponentState } from "./state.js";
 import {
@@ -32,7 +30,7 @@ import {
   LoadingErrorView,
   NoAccountToDepositView,
   NoEnoughBalanceView,
-  ReadyView,
+  ReadyView
 } from "./views.js";
 
 export interface Props {
@@ -119,6 +117,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const DepositPage = compose(
   "DepositPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index bbf2c2771..5ad0841dc 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -21,18 +21,18 @@ import {
   KnownBankAccountsInfo,
   parsePaytoUri,
   PaytoUri,
-  stringifyPaytoUri,
+  stringifyPaytoUri
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const parsed = amountStr === undefined ? undefined : 
Amounts.parse(amountStr);
   const currency = parsed !== undefined ? parsed.currency : currencyStr;
 
@@ -55,8 +55,8 @@ export function useComponentState(
     parsed !== undefined
       ? parsed
       : currency !== undefined
-      ? Amounts.zeroOfCurrency(currency)
-      : undefined;
+        ? Amounts.zeroOfCurrency(currency)
+        : undefined;
   // const [accountIdx, setAccountIdx] = useState<number>(0);
   const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as 
any));
   const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
@@ -134,7 +134,7 @@ export function useComponentState(
   const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
 
   if (fee === undefined) {
-    getFeeForAmount(currentAccount, amount, api).then((initialFee) => {
+    getFeeForAmount(currentAccount, amount, api.wallet).then((initialFee) => {
       setFee(initialFee);
     });
     return {
@@ -149,7 +149,7 @@ export function useComponentState(
     const uri = !accountStr ? undefined : parsePaytoUri(accountStr);
     if (uri) {
       try {
-        const result = await getFeeForAmount(uri, amount, api);
+        const result = await getFeeForAmount(uri, amount, api.wallet);
         setSelectedAccount(uri);
         setFee(result);
       } catch (e) {
@@ -162,7 +162,7 @@ export function useComponentState(
   async function updateAmount(newAmount: AmountJson): Promise<void> {
     // const parsed = Amounts.parse(`${currency}:${numStr}`);
     try {
-      const result = await getFeeForAmount(currentAccount, newAmount, api);
+      const result = await getFeeForAmount(currentAccount, newAmount, 
api.wallet);
       setAmount(newAmount);
       setFee(result);
     } catch (e) {
@@ -185,8 +185,8 @@ export function useComponentState(
   const amountError = !isDirty
     ? undefined
     : Amounts.cmp(balance, amount) === -1
-    ? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
-    : undefined;
+      ? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
+      : undefined;
 
   const unableToDeposit =
     Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee
@@ -243,11 +243,11 @@ export function useComponentState(
 async function getFeeForAmount(
   p: PaytoUri,
   a: AmountJson,
-  api: typeof wxApi,
+  wallet: ReturnType<typeof useBackendContext>["wallet"],
 ): Promise<DepositGroupFees> {
   const depositPaytoUri = `payto://${p.targetType}/${p.targetPath}`;
   const amount = Amounts.stringify(a);
-  return await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
+  return await wallet.call(WalletApiOperation.GetFeeForDeposit, {
     amount,
     depositPaytoUri,
   });
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 3f08c678c..90ac020b7 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -23,14 +23,13 @@ import {
   Amounts,
   DepositGroupFees,
   parsePaytoUri,
-  stringifyPaytoUri,
+  stringifyPaytoUri
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
+import { tests } from "../../../../web-util/src/index.browser.js";
 import {
-  createWalletApiMock,
-  mountHook,
-  nullFunction,
+  createWalletApiMock, nullFunction
 } from "../../test-utils.js";
 
 import { useComponentState } from "./state.js";
@@ -50,7 +49,7 @@ const withSomeFee = (): DepositGroupFees => ({
 
 describe("DepositPage states", () => {
   it("should have status 'no-enough-balance' when balance is empty", async () 
=> {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = { currency, onCancel: nullFunction, onSuccess: nullFunction 
};
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
@@ -72,27 +71,21 @@ describe("DepositPage states", () => {
       },
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("no-enough-balance");
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      ({ status }) => {
+        expect(status).equal("no-enough-balance");
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should have status 'no-accounts' when balance is not empty and accounts 
is empty", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = { currency, onCancel: nullFunction, onSuccess: nullFunction 
};
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
@@ -113,22 +106,17 @@ describe("DepositPage states", () => {
         accounts: [],
       },
     );
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "no-accounts") expect.fail();
-      // expect(r.cancelHandler.onClick).not.undefined;
-    }
-
-    await assertNoPendingUpdate();
+
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      ({ status }) => {
+        expect(status).equal("no-accounts");
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
@@ -146,7 +134,7 @@ describe("DepositPage states", () => {
   };
 
   it("should have status 'ready' but unable to deposit ", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = { currency, onCancel: nullFunction, onSuccess: nullFunction 
};
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
@@ -173,37 +161,29 @@ describe("DepositPage states", () => {
       withoutFee(),
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
-      expect(r.depositHandler.onClick).undefined;
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+        expect(state.depositHandler.onClick).undefined;
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
   });
 
   it("should not be able to deposit more than the balance ", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = { currency, onCancel: nullFunction, onSuccess: nullFunction 
};
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
@@ -235,134 +215,45 @@ describe("DepositPage states", () => {
       undefined,
       withoutFee(),
     );
-    handler.addWalletCallResponse(
-      WalletApiOperation.GetFeeForDeposit,
-      undefined,
-      withoutFee(),
-    );
-    handler.addWalletCallResponse(
-      WalletApiOperation.GetFeeForDeposit,
-      undefined,
-      withoutFee(),
-    );
-
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
 
-    expect(await waitForStateUpdate()).true;
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
     const accountSelected = stringifyPaytoUri(ibanPayto.uri);
 
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
-      expect(r.depositHandler.onClick).undefined;
-      expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-      expect(r.account.onChange).not.undefined;
-
-      r.account.onChange!(accountSelected);
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
-      expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-      expect(r.depositHandler.onClick).undefined;
-    }
-
-    await assertNoPendingUpdate();
-  });
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+        expect(state.depositHandler.onClick).undefined;
+        expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
+        expect(state.account.onChange).not.undefined;
+
+        state.account.onChange!(accountSelected);
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(accountSelected);
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+        expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
+        expect(state.depositHandler.onClick).undefined;
+      },
+    ], TestingContext)
 
-  // it("should calculate the fee upon entering amount ", async () => {
-  //   const { pullLastResultOrThrow, waitForStateUpdate, 
assertNoPendingUpdate } =
-  //     mountHook(() =>
-  //       useComponentState(
-  //         { currency, onCancel: nullFunction, onSuccess: nullFunction },
-  //         {
-  //           getBalance: async () =>
-  //           ({
-  //             balances: [{ available: `${currency}:1` }],
-  //           } as Partial<BalancesResponse>),
-  //           listKnownBankAccounts: async () => ({ accounts: [ibanPayto] }),
-  //           getFeeForDeposit: withSomeFee,
-  //         } as Partial<typeof wxApi> 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(accountSelected);
-  //     expect(r.amount.value).eq("0");
-  //     expect(r.depositHandler.onClick).undefined;
-  //     expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-
-  //     r.amount.onInput("10");
-  //   }
-
-  //   expect(await waitForStateUpdate()).true;
-
-  //   {
-  //     const r = pullLastResultOrThrow();
-  //     if (r.status !== "ready") expect.fail();
-  //     expect(r.cancelHandler.onClick).not.undefined;
-  //     expect(r.currency).eq(currency);
-  //     expect(r.account.value).eq(accountSelected);
-  //     expect(r.amount.value).eq("10");
-  //     expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-  //     expect(r.depositHandler.onClick).undefined;
-
-  //     r.amount.onInput("3");
-  //   }
-
-  //   expect(await waitForStateUpdate()).true;
-
-  //   {
-  //     const r = pullLastResultOrThrow();
-  //     if (r.status !== "ready") expect.fail();
-  //     expect(r.cancelHandler.onClick).not.undefined;
-  //     expect(r.currency).eq(currency);
-  //     expect(r.account.value).eq(accountSelected);
-  //     expect(r.amount.value).eq("3");
-  //     expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-  //     expect(r.depositHandler.onClick).not.undefined;
-  //   }
-
-  //   await assertNoPendingUpdate();
-  //   expect(handler.getCallingQueueState()).eq("empty")
-  // });
+    expect(hookBehavior).deep.equal({ result: "ok" })
+    expect(handler.getCallingQueueState()).eq("empty");
+  });
 
   it("should calculate the fee upon entering amount ", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
     const props = { currency, onCancel: nullFunction, onSuccess: nullFunction 
};
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
@@ -399,70 +290,54 @@ describe("DepositPage states", () => {
       withSomeFee(),
     );
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(() => useComponentState(props, mock));
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const { status } = pullLastResultOrThrow();
-      expect(status).equal("loading");
-    }
-
-    expect(await waitForStateUpdate()).true;
     const accountSelected = stringifyPaytoUri(ibanPayto.uri);
 
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
-      expect(r.depositHandler.onClick).undefined;
-      expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
-      expect(r.account.onChange).not.undefined;
-
-      r.account.onChange!(accountSelected);
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
-      expect(r.depositHandler.onClick).undefined;
-      expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
-
-      expect(r.amount.onInput).not.undefined;
-      if (!r.amount.onInput) return;
-      r.amount.onInput(Amounts.parseOrThrow("EUR:10"));
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const r = pullLastResultOrThrow();
-      if (r.status !== "ready") expect.fail();
-      expect(r.cancelHandler.onClick).not.undefined;
-      expect(r.currency).eq(currency);
-      expect(r.account.value).eq(accountSelected);
-      expect(r.amount.value).deep.eq(Amounts.parseOrThrow("EUR: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;
-    }
-
-    await assertNoPendingUpdate();
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+        expect(state.depositHandler.onClick).undefined;
+        expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
+        expect(state.account.onChange).not.undefined;
+
+        state.account.onChange!(accountSelected);
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(accountSelected);
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
+        expect(state.depositHandler.onClick).undefined;
+        expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
+
+        expect(state.amount.onInput).not.undefined;
+        if (!state.amount.onInput) return;
+        state.amount.onInput(Amounts.parseOrThrow("EUR:10"));
+      },
+      (state) => {
+        if (state.status !== "ready") expect.fail();
+        expect(state.cancelHandler.onClick).not.undefined;
+        expect(state.currency).eq(currency);
+        expect(state.account.value).eq(accountSelected);
+        expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
+        expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
+        
expect(state.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`));
+        expect(state.depositHandler.onClick).not.undefined;
+      },
+    ], TestingContext)
+
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
index 2f066d744..f1e766a18 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
@@ -18,7 +18,6 @@ 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";
 
@@ -88,6 +87,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const DestinationSelectionPage = compose(
   "DestinationSelectionPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
index a67f926bc..0621d3304 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -17,15 +17,15 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 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";
 
 export function useComponentState(
   props: Props,
-  api: typeof wxApi,
 ): RecursiveState<State> {
+  const api = useBackendContext()
   const parsedInitialAmount = !props.amount
     ? undefined
     : Amounts.parse(props.amount);
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
index c2aa04849..afba5db35 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
@@ -23,11 +23,12 @@ import {
   Amounts,
   ExchangeEntryStatus,
   ExchangeListItem,
-  ExchangeTosStatus,
+  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 { tests } from "../../../../web-util/src/index.browser.js";
+import { createWalletApiMock, nullFunction } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 const exchangeArs: ExchangeListItem = {
@@ -42,7 +43,7 @@ const exchangeArs: ExchangeListItem = {
 
 describe("Destination selection states", () => {
   it("should select currency if no amount specified", async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
     handler.addWalletCallResponse(
       WalletApiOperation.ListExchanges,
@@ -54,83 +55,65 @@ describe("Destination selection states", () => {
 
     const props = {
       type: "get" as const,
-      goToWalletManualWithdraw: () => {
-        return null;
-      },
-      goToWalletWalletInvoice: () => {
-        null;
-      },
+      goToWalletManualWithdraw: nullFunction,
+      goToWalletWalletInvoice: nullFunction,
     };
-    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);
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      ({ status }) => {
+        expect(status).equal("loading");
+      },
+      (state) => {
+        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!);
+      },
+      (state) => {
+        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"));
-    }
+        
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0"));
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 
   it("should be possible to start with an amount specified in request params", 
async () => {
-    const { handler, mock } = createWalletApiMock();
+    const { handler, TestingContext } = createWalletApiMock();
 
     const props = {
       type: "get" as const,
-      goToWalletManualWithdraw: () => {
-        return null;
-      },
-      goToWalletWalletInvoice: () => {
-        null;
-      },
+      goToWalletManualWithdraw: nullFunction,
+      goToWalletWalletInvoice: nullFunction,
       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"));
-    }
+    const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, 
props, [
+      // ({ status }) => {
+      //   expect(status).equal("loading");
+      // },
+      (state) => {
+        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"));
+      },
+    ], TestingContext)
 
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" })
     expect(handler.getCallingQueueState()).eq("empty");
+
   });
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 2333fd3c1..c42798c8f 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -31,12 +31,12 @@ import { useEffect, useRef, useState } from "preact/hooks";
 import { Diagnostics } from "../components/Diagnostics.js";
 import { NotifyUpdateFadeOut } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { useDiagnostics } from "../hooks/useDiagnostics.js";
 import { Button } from "../mui/Button.js";
 import { Grid } from "../mui/Grid.js";
-import { wxApi } from "../wxApi.js";
 
 export function DeveloperPage(): VNode {
   const [status, timedOut] = useDiagnostics();
@@ -45,13 +45,15 @@ export function DeveloperPage(): VNode {
   //FIXME: waiting for retry notification make a always increasing loop of 
notifications
   listenAllEvents.includes = (e) => e !== "waiting-for-retry"; // includes 
every event
 
+  const api = useBackendContext();
+
   const response = useAsyncAsHook(async () => {
-    const op = await wxApi.wallet.call(
+    const op = await api.wallet.call(
       WalletApiOperation.GetPendingOperations,
       {},
     );
-    const c = await wxApi.wallet.call(WalletApiOperation.DumpCoins, {});
-    const ex = await wxApi.wallet.call(WalletApiOperation.ListExchanges, {});
+    const c = await api.wallet.call(WalletApiOperation.DumpCoins, {});
+    const ex = await api.wallet.call(WalletApiOperation.ListExchanges, {});
     return {
       operations: op.pendingOperations,
       coins: c.coins,
@@ -60,10 +62,7 @@ export function DeveloperPage(): VNode {
   });
 
   useEffect(() => {
-    return wxApi.listener.onUpdateNotification(
-      listenAllEvents,
-      response?.retry,
-    );
+    return api.listener.onUpdateNotification(listenAllEvents, response?.retry);
   });
 
   const nonResponse = { operations: [], coins: [], exchanges: [] };
@@ -82,7 +81,7 @@ export function DeveloperPage(): VNode {
       coins={coins}
       exchanges={exchanges}
       onDownloadDatabase={async () => {
-        const db = await wxApi.wallet.call(WalletApiOperation.ExportDb, {});
+        const db = await api.wallet.call(WalletApiOperation.ExportDb, {});
         return JSON.stringify(db);
       }}
     />
@@ -135,9 +134,10 @@ export function View({
       content,
     });
   }
+  const api = useBackendContext();
   const fileRef = useRef<HTMLInputElement>(null);
   async function onImportDatabase(str: string): Promise<void> {
-    return wxApi.wallet.call(WalletApiOperation.ImportDb, {
+    return api.wallet.call(WalletApiOperation.ImportDb, {
       dump: JSON.parse(str),
     });
   }
@@ -177,7 +177,7 @@ export function View({
             onClick={() =>
               confirmReset(
                 i18n.str`Do you want to IRREVOCABLY DESTROY everything inside 
your wallet and LOSE ALL YOUR COINS?`,
-                () => wxApi.background.resetDb(),
+                () => api.background.resetDb(),
               )
             }
           >
@@ -190,7 +190,7 @@ export function View({
             onClick={() =>
               confirmReset(
                 i18n.str`TESTING: This may delete all your coin, proceed with 
caution`,
-                () => wxApi.background.runGarbageCollector(),
+                () => api.background.runGarbageCollector(),
               )
             }
           >
diff --git 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
index 4b7725264..95badb218 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
@@ -17,7 +17,6 @@
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -55,6 +54,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const ComponentName = compose(
   "ComponentName",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/state.ts 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/state.ts
index d194b3f97..31a351579 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/state.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/state.ts
@@ -14,10 +14,9 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
-export function useComponentState({ p }: Props, api: typeof wxApi): State {
+export function useComponentState({ p }: Props): State {
   return {
     status: "ready",
     error: undefined,
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx
index a0c62787a..d8a7c6090 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx
@@ -21,9 +21,9 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../context/backend.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { queryToSlashKeys } from "../utils/index.js";
-import { wxApi } from "../wxApi.js";
 import { ExchangeAddConfirmPage } from "./ExchangeAddConfirm.js";
 import { ExchangeSetUrlPage } from "./ExchangeSetUrl.js";
 
@@ -37,8 +37,9 @@ export function ExchangeAddPage({ currency, onBack }: Props): 
VNode {
     { url: string; config: TalerConfigResponse } | undefined
   >(undefined);
 
+  const api = useBackendContext();
   const knownExchangesResponse = useAsyncAsHook(() =>
-    wxApi.wallet.call(WalletApiOperation.ListExchanges, {}),
+    api.wallet.call(WalletApiOperation.ListExchanges, {}),
   );
   const knownExchanges = !knownExchangesResponse
     ? []
@@ -75,7 +76,7 @@ export function ExchangeAddPage({ currency, onBack }: Props): 
VNode {
       url={verifying.url}
       onCancel={onBack}
       onConfirm={async () => {
-        await wxApi.wallet.call(WalletApiOperation.AddExchange, {
+        await api.wallet.call(WalletApiOperation.AddExchange, {
           exchangeBaseUrl: canonicalizeBaseUrl(verifying.url),
           forceUpdate: true,
         });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
index a95830f8e..661fa5286 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
@@ -18,14 +18,13 @@ import {
   DenomOperationMap,
   ExchangeFullDetails,
   ExchangeListItem,
-  FeeDescriptionPair,
+  FeeDescriptionPair
 } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { State as SelectExchangeState } from 
"../../hooks/useSelectedExchange.js";
 import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import {
   ComparingView,
@@ -33,7 +32,7 @@ import {
   NoExchangesView,
   PrivacyContentView,
   ReadyView,
-  TosContentView,
+  TosContentView
 } from "./views.js";
 
 export interface Props {
@@ -106,6 +105,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const ExchangeSelectionPage = compose(
   "ExchangeSelectionPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
index 63d545b97..585050413 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
@@ -17,17 +17,17 @@
 import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util";
 import {
   createPairTimeline,
-  WalletApiOperation,
+  WalletApiOperation
 } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
   { onCancel, onSelection, list: exchanges, currentExchange }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const initialValue = exchanges.findIndex(
     (e) => e.exchangeBaseUrl === currentExchange,
   );
@@ -52,14 +52,14 @@ export function useComponentState(
     const selected = !selectedExchange
       ? undefined
       : await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
-          exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
-        });
+        exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
+      });
 
     const original = !initialExchange
       ? undefined
       : await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
-          exchangeBaseUrl: initialExchange.exchangeBaseUrl,
-        });
+        exchangeBaseUrl: initialExchange.exchangeBaseUrl,
+      });
 
     return {
       exchanges,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 4b9c5c711..50f634f52 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -33,13 +33,13 @@ import {
 } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
 import { TransactionItem } from "../components/TransactionItem.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { NoBalanceHelp } from "../popup/NoBalanceHelp.js";
 import DownloadIcon from "../svg/download_24px.svg";
 import UploadIcon from "../svg/upload_24px.svg";
-import { wxApi } from "../wxApi.js";
 
 interface Props {
   currency?: string;
@@ -52,13 +52,14 @@ export function HistoryPage({
   goToWalletDeposit,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
+  const api = useBackendContext();
   const state = useAsyncAsHook(async () => ({
-    b: await wxApi.wallet.call(WalletApiOperation.GetBalances, {}),
-    tx: await wxApi.wallet.call(WalletApiOperation.GetTransactions, {}),
+    b: await api.wallet.call(WalletApiOperation.GetBalances, {}),
+    tx: await api.wallet.call(WalletApiOperation.GetTransactions, {}),
   }));
 
   useEffect(() => {
-    return wxApi.listener.onUpdateNotification(
+    return api.listener.onUpdateNotification(
       [NotificationType.WithdrawGroupFinished],
       state?.retry,
     );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
index df4e7586f..0ee6472d6 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
@@ -20,10 +20,9 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
 import {
   ButtonHandler,
   SelectFieldHandler,
-  TextFieldHandler,
+  TextFieldHandler
 } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -75,6 +74,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const ManageAccountPage = compose(
   "ManageAccountPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
index 1f920f05f..d60ef962b 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
@@ -17,18 +17,18 @@
 import {
   KnownBankAccountsInfo,
   parsePaytoUri,
-  stringifyPaytoUri,
+  stringifyPaytoUri
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { AccountByType, Props, State } from "./index.js";
 
 export function useComponentState(
   { currency, onAccountAdded, onCancel }: Props,
-  api: typeof wxApi,
 ): State {
+  const api = useBackendContext()
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
   );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts 
b/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
index 253a0e629..3791b8967 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
@@ -18,11 +18,10 @@ import { UserAttentionUnreadList } from 
"@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { compose, StateViewMap } from "../../utils/index.js";
-import { wxApi } from "../../wxApi.js";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
-export interface Props {}
+export type Props = object
 
 export type State = State.Loading | State.LoadingUriError | State.Ready;
 
@@ -56,6 +55,6 @@ const viewMapping: StateViewMap<State> = {
 
 export const NotificationsPage = compose(
   "NotificationsPage",
-  (p: Props) => useComponentState(p, wxApi),
+  (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts 
b/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
index 093722cf0..1042dea9f 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
@@ -15,11 +15,12 @@
  */
 
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useBackendContext } from "../../context/backend.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
-import { wxApi } from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
-export function useComponentState({}: Props, api: typeof wxApi): State {
+export function useComponentState(p: Props): State {
+  const api = useBackendContext()
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(
       WalletApiOperation.GetUserAttentionRequests,
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index d5f072828..eb86c9a3f 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -31,10 +31,10 @@ import {
   SubTitle,
   Title,
 } from "../components/styled/index.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { Button } from "../mui/Button.js";
 import { queryToSlashConfig } from "../utils/index.js";
-import { wxApi } from "../wxApi.js";
 
 interface Props {
   currency: string;
@@ -46,7 +46,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
     | { url: string; name: string; provider: BackupBackupProviderTerms }
     | undefined
   >(undefined);
-
+  const api = useBackendContext();
   if (!verifying) {
     return (
       <SetUrlView
@@ -70,7 +70,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
         setVerifying(undefined);
       }}
       onConfirm={() => {
-        return wxApi.wallet
+        return api.wallet
           .call(WalletApiOperation.AddBackupProvider, {
             backupProviderBaseUrl: verifying.url,
             name: verifying.name,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index 6dde30b39..46d54e871 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -28,10 +28,10 @@ import { Loading } from "../components/Loading.js";
 import { LoadingError } from "../components/LoadingError.js";
 import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
-import { wxApi } from "../wxApi.js";
 
 interface Props {
   pid: string;
@@ -47,12 +47,10 @@ export function ProviderDetailPage({
   onWithdraw,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
+  const api = useBackendContext();
   async function getProviderInfo(): Promise<ProviderInfo | null> {
     //create a first list of backup info by currency
-    const status = await wxApi.wallet.call(
-      WalletApiOperation.GetBackupInfo,
-      {},
-    );
+    const status = await api.wallet.call(WalletApiOperation.GetBackupInfo, {});
 
     const providers = status.providers.filter(
       (p) => p.syncProviderBaseUrl === providerURL,
@@ -103,7 +101,7 @@ export function ProviderDetailPage({
     <ProviderView
       info={info}
       onSync={async () =>
-        wxApi.wallet
+        api.wallet
           .call(WalletApiOperation.RunBackupCycle, {
             providers: [providerURL],
           })
@@ -120,7 +118,7 @@ export function ProviderDetailPage({
         onWithdraw(info.paymentStatus.amount);
       }}
       onDelete={() =>
-        wxApi.wallet
+        api.wallet
           .call(WalletApiOperation.RemoveBackupProvider, {
             provider: providerURL,
           })
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index c0268a1ae..f00b242d6 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -34,6 +34,7 @@ import {
   SuccessText,
   WarningText,
 } from "../components/styled/index.js";
+import { useBackendContext } from "../context/backend.js";
 import { useDevContext } from "../context/devContext.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -43,7 +44,6 @@ import { useClipboardPermissions } from 
"../hooks/useClipboardPermissions.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { Pages } from "../NavigationBar.js";
 import { platform } from "../platform/api.js";
-import { wxApi } from "../wxApi.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
 
@@ -53,10 +53,11 @@ export function SettingsPage(): VNode {
   const { devModeToggle } = useDevContext();
   const { name, update } = useBackupDeviceName();
   const webex = platform.getWalletWebExVersion();
+  const api = useBackendContext();
 
   const exchangesHook = useAsyncAsHook(async () => {
-    const list = await wxApi.wallet.call(WalletApiOperation.ListExchanges, {});
-    const version = await wxApi.wallet.call(WalletApiOperation.GetVersion, {});
+    const list = await api.wallet.call(WalletApiOperation.ListExchanges, {});
+    const version = await api.wallet.call(WalletApiOperation.GetVersion, {});
     return { exchanges: list.exchanges, version };
   });
   const { exchanges, version } =
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index e70f5fbd1..b7eb4a947 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -60,11 +60,11 @@ import {
   WarningBox,
 } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
+import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { Pages } from "../NavigationBar.js";
-import { wxApi } from "../wxApi.js";
 
 interface Props {
   tid: string;
@@ -76,17 +76,17 @@ export function TransactionPage({
   goToWalletHistory,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
-
+  const api = useBackendContext();
   const state = useAsyncAsHook(
     () =>
-      wxApi.wallet.call(WalletApiOperation.GetTransactionById, {
+      api.wallet.call(WalletApiOperation.GetTransactionById, {
         transactionId,
       }),
     [transactionId],
   );
 
   useEffect(() =>
-    wxApi.listener.onUpdateNotification(
+    api.listener.onUpdateNotification(
       [NotificationType.WithdrawGroupFinished],
       state?.retry,
     ),
@@ -118,19 +118,19 @@ export function TransactionPage({
         null;
       }}
       onDelete={async () => {
-        await wxApi.wallet.call(WalletApiOperation.DeleteTransaction, {
+        await api.wallet.call(WalletApiOperation.DeleteTransaction, {
           transactionId,
         });
         goToWalletHistory(currency);
       }}
       onRetry={async () => {
-        await wxApi.wallet.call(WalletApiOperation.RetryTransaction, {
+        await api.wallet.call(WalletApiOperation.RetryTransaction, {
           transactionId,
         });
         goToWalletHistory(currency);
       }}
       onRefund={async (purchaseId) => {
-        await wxApi.wallet.call(WalletApiOperation.ApplyRefundFromPurchaseId, {
+        await api.wallet.call(WalletApiOperation.ApplyRefundFromPurchaseId, {
           purchaseId,
         });
       }}
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index 524eed990..d304a42a5 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -151,6 +151,14 @@ function onUpdateNotification(
   return platform.listenToWalletBackground(onNewMessage);
 }
 
+export type WxApiType = {
+  wallet: WalletCoreApiClient;
+  background: BackgroundApiClient;
+  listener: {
+    onUpdateNotification: typeof onUpdateNotification;
+  }
+}
+
 export const wxApi = {
   wallet: new WxWalletCoreApiClient(),
   background: new BackgroundApiClient(),
diff --git a/packages/web-util/src/tests/hook.ts 
b/packages/web-util/src/tests/hook.ts
index f5bebbd6d..a4938f3f9 100644
--- a/packages/web-util/src/tests/hook.ts
+++ b/packages/web-util/src/tests/hook.ts
@@ -246,7 +246,7 @@ interface HookTestResultError {
 export async function hookBehaveLikeThis<T extends object, PropsType>(
   hookFunction: (p: PropsType) => RecursiveState<T>,
   props: PropsType,
-  checks: Array<(state: T) => void>,
+  checks: Array<(state: Exclude<T, VoidFunction>) => void>,
   Context?: ({ children }: { children: any }) => VNode | null,
 ): Promise<HookTestResult> {
   const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =

-- 
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]