gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 02/02: fix #7153: more error handling


From: gnunet
Subject: [taler-wallet-core] 02/02: fix #7153: more error handling
Date: Tue, 10 Jan 2023 00:20:21 +0100

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

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

commit 4a781bd0dd8828ce152f6ab2c3f1bbd6b5e826f7
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Jan 9 20:20:09 2023 -0300

    fix #7153: more error handling
    
    if handler do not trap error then fail at compile time,
    all safe handlers push alert on error
    errors are typed so they render good information
---
 .../src/components/AmountField.stories.tsx         |   8 +-
 .../src/components/CurrentAlerts.tsx               |  75 ++++++++---
 .../src/components/PendingTransactions.stories.tsx |   8 +-
 .../src/components/QR.stories.tsx                  |   4 +-
 .../ShowFullContractTermPopup.stories.tsx          |  10 +-
 .../src/components/ShowFullContractTermPopup.tsx   |  13 +-
 .../src/components/TermsOfService/state.ts         |  42 +++---
 .../src/components/TermsOfService/stories.tsx      |   4 +-
 .../taler-wallet-webextension/src/context/alert.ts |  98 +++++++++++---
 .../src/context/devContext.ts                      |  17 +--
 .../src/cta/Deposit/state.ts                       |   5 +-
 .../src/cta/Deposit/stories.tsx                    |   4 +-
 .../src/cta/Deposit/test.ts                        |   2 +-
 .../src/cta/InvoiceCreate/state.ts                 |  54 +++-----
 .../src/cta/InvoiceCreate/stories.tsx              |   9 +-
 .../src/cta/InvoicePay/index.ts                    |   1 -
 .../src/cta/InvoicePay/state.ts                    |  37 ++----
 .../src/cta/InvoicePay/stories.tsx                 |   4 +-
 .../src/cta/InvoicePay/views.tsx                   |  22 +--
 .../src/cta/Payment/state.ts                       |  69 +++++-----
 .../src/cta/Payment/stories.tsx                    |  40 +++---
 .../src/cta/Payment/test.ts                        |  42 +++---
 .../src/cta/Recovery/state.ts                      |   7 +-
 .../src/cta/Recovery/stories.tsx                   |   2 +-
 .../src/cta/Refund/state.ts                        |   7 +-
 .../src/cta/Refund/stories.tsx                     |  10 +-
 .../src/cta/Refund/test.ts                         |   9 +-
 .../taler-wallet-webextension/src/cta/Tip/state.ts |   7 +-
 .../src/cta/Tip/stories.tsx                        |   6 +-
 .../taler-wallet-webextension/src/cta/Tip/test.ts  |   5 +-
 .../src/cta/TransferCreate/index.ts                |   1 -
 .../src/cta/TransferCreate/state.ts                |  46 +++----
 .../src/cta/TransferCreate/stories.tsx             |   9 +-
 .../src/cta/TransferCreate/views.tsx               |   8 --
 .../src/cta/TransferPickup/index.ts                |   1 -
 .../src/cta/TransferPickup/state.ts                |  33 ++---
 .../src/cta/TransferPickup/stories.tsx             |   4 +-
 .../src/cta/TransferPickup/views.tsx               |   8 --
 .../src/cta/Withdraw/state.ts                      |   9 +-
 .../src/cta/Withdraw/stories.tsx                   |  49 ++-----
 .../src/cta/Withdraw/test.ts                       |   3 +-
 .../src/cta/Withdraw/views.tsx                     |   8 --
 .../src/hooks/useAsyncAsHook.ts                    |   6 +-
 .../src/hooks/useAutoOpenPermissions.ts            |  69 ++++------
 .../src/hooks/useClipboardPermissions.ts           |  69 ++++------
 .../src/hooks/useSelectedExchange.ts               |   8 +-
 .../src/hooks/useTalerActionURL.test.ts            |  56 ++++----
 .../src/hooks/useWalletDevMode.ts                  |  44 +++---
 .../taler-wallet-webextension/src/mui/handlers.ts  |  40 +++++-
 .../src/popup/Application.tsx                      |   8 +-
 .../src/popup/Balance.stories.tsx                  |  12 +-
 .../src/popup/BalancePage.tsx                      |  13 +-
 .../src/popup/TalerActionFound.stories.tsx         |  14 +-
 .../taler-wallet-webextension/src/stories.test.ts  |  19 ++-
 .../taler-wallet-webextension/src/test-utils.ts    |   5 +-
 .../src/wallet/AddBackupProvider/index.ts          |   2 +-
 .../src/wallet/AddBackupProvider/state.ts          |  33 ++---
 .../src/wallet/AddBackupProvider/stories.tsx       |  16 +--
 .../src/wallet/AddBackupProvider/test.ts           |   3 +-
 .../src/wallet/AddNewActionView.stories.tsx        |   4 +-
 .../src/wallet/Backup.stories.tsx                  |  10 +-
 .../src/wallet/BackupPage.tsx                      |   6 +-
 .../src/wallet/DepositPage/state.ts                |  23 ++--
 .../src/wallet/DepositPage/stories.tsx             |  51 +++----
 .../src/wallet/DepositPage/test.ts                 |   3 +-
 .../src/wallet/DestinationSelection/state.ts       |  31 ++---
 .../src/wallet/DestinationSelection/stories.tsx    |   8 +-
 .../src/wallet/DestinationSelection/test.ts        |   3 +-
 .../src/wallet/DeveloperPage.stories.tsx           |   4 +-
 .../src/wallet/EmptyComponentExample/stories.tsx   |   4 +-
 .../src/wallet/ExchangeAddConfirm.stories.tsx      |   8 +-
 .../src/wallet/ExchangeAddSetUrl.stories.tsx       |  10 +-
 .../src/wallet/ExchangeSelection/state.ts          |  41 +++---
 .../src/wallet/ExchangeSelection/stories.tsx       |  14 +-
 .../src/wallet/History.stories.tsx                 |  72 +++++-----
 .../src/wallet/History.tsx                         |  11 +-
 .../src/wallet/ManageAccount/state.ts              |  19 +--
 .../src/wallet/ManageAccount/stories.tsx           |  13 +-
 .../src/wallet/Notifications/stories.tsx           |   4 +-
 .../wallet/ProviderAddConfirmProvider.stories.tsx  |   6 +-
 .../src/wallet/ProviderAddSetUrl.stories.tsx       |  12 +-
 .../src/wallet/ProviderDetail.stories.tsx          |  97 +++++++-------
 .../src/wallet/ProviderDetailPage.tsx              |   6 +-
 .../src/wallet/QrReader.stories.tsx                |   4 +-
 .../src/wallet/ReserveCreated.stories.tsx          |  14 +-
 .../src/wallet/Settings.stories.tsx                |  97 ++++++++------
 .../src/wallet/Settings.tsx                        |  24 ++--
 .../src/wallet/Transaction.stories.tsx             | 147 +++++++++++----------
 .../src/wallet/Transaction.tsx                     |   6 +-
 .../src/wallet/Welcome.stories.tsx                 |   8 +-
 packages/taler-wallet-webextension/src/wxApi.ts    |  26 ++--
 91 files changed, 999 insertions(+), 1004 deletions(-)

diff --git 
a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
index 61c4a7661..f253d1996 100644
--- a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
+++ b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
@@ -20,12 +20,10 @@
  */
 
 import { AmountJson, Amounts } from "@gnu-taler/taler-util";
-import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { useTranslationContext } from "../context/translation.js";
-import { Grid } from "../mui/Grid.js";
-import { AmountFieldHandler, TextFieldHandler } from "../mui/handlers.js";
+import { AmountFieldHandler, nullFunction, withSafe } from 
"../mui/handlers.js";
 import { AmountField } from "./AmountField.js";
 
 export default {
@@ -39,9 +37,9 @@ function RenderAmount(): VNode {
 
   const handler: AmountFieldHandler = {
     value: value ?? Amounts.zeroOfCurrency("USD"),
-    onInput: async (e) => {
+    onInput: withSafe(async (e) => {
       setValue(e);
-    },
+    }, nullFunction),
     error,
   };
   const { i18n } = useTranslationContext();
diff --git 
a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx 
b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
index a56c82dee..47863d73e 100644
--- a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
+++ b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
@@ -18,7 +18,6 @@ import { ComponentChildren, Fragment, h, VNode } from 
"preact";
 import { useState } from "preact/hooks";
 import { useTranslationContext } from "../../../web-util/src/index.browser.js";
 import {
-  ErrorAlert,
   Alert as AlertNotification,
   useAlertContext,
 } from "../context/alert.js";
@@ -37,41 +36,78 @@ function AlertContext({
   context: undefined | object;
 }): VNode {
   const [more, setMore] = useState(false);
+  const [wrap, setWrap] = useState(false);
   const { i18n } = useTranslationContext();
   if (!more) {
     return (
       <div style={{ display: "flex", justifyContent: "right" }}>
-        <a onClick={() => setMore(true)}>
+        <a
+          onClick={() => setMore(true)}
+          style={{ cursor: "pointer", textDecoration: "underline" }}
+        >
           <i18n.Translate>more info</i18n.Translate>
         </a>
       </div>
     );
   }
+  const errorInfo = JSON.stringify(
+    context === undefined ? { cause } : { context, cause },
+    undefined,
+    2,
+  );
   return (
-    <pre style={{ overflow: "overlay" }}>
-      {JSON.stringify(
-        context === undefined ? { cause } : { context, cause },
-        undefined,
-        2,
-      )}
-    </pre>
+    <Fragment>
+      <div style={{ display: "flex", justifyContent: "right" }}>
+        <a
+          onClick={() => setWrap(!wrap)}
+          style={{ cursor: "pointer", textDecoration: "underline" }}
+        >
+          <i18n.Translate>wrap text</i18n.Translate>
+        </a>
+        &nbsp;&nbsp;
+        <a
+          onClick={() => navigator.clipboard.writeText(errorInfo)}
+          style={{ cursor: "pointer", textDecoration: "underline" }}
+        >
+          <i18n.Translate>copy content</i18n.Translate>
+        </a>
+        &nbsp;&nbsp;
+        <a
+          onClick={() => setMore(false)}
+          style={{ cursor: "pointer", textDecoration: "underline" }}
+        >
+          <i18n.Translate>less info</i18n.Translate>
+        </a>
+      </div>
+      <pre
+        style={
+          wrap
+            ? {
+                whiteSpace: "pre-wrap",
+                overflowWrap: "anywhere",
+              }
+            : {
+                overflow: "overlay",
+              }
+        }
+      >
+        {errorInfo}
+      </pre>
+    </Fragment>
   );
 }
 
 export function ErrorAlertView({
-  error: alert,
+  error,
   onClose,
 }: {
-  error: ErrorAlert;
+  error: AlertNotification;
   onClose?: () => Promise<void>;
 }): VNode {
   return (
-    <Alert title={alert.message} severity={alert.type} onClose={onClose}>
-      <div style={{ display: "flex", flexDirection: "column" }}>
-        <div>{alert.description}</div>
-        <AlertContext context={alert.context} cause={alert.cause} />
-      </div>
-    </Alert>
+    <Wrapper>
+      <AlertView alert={error} onClose={onClose} />
+    </Wrapper>
   );
 }
 
@@ -86,6 +122,9 @@ export function AlertView({
     <Alert title={alert.message} severity={alert.type} onClose={onClose}>
       <div style={{ display: "flex", flexDirection: "column" }}>
         <div>{alert.description}</div>
+        {alert.type === "error" ? (
+          <AlertContext context={alert.context} cause={alert.cause} />
+        ) : undefined}
       </div>
     </Alert>
   );
@@ -104,5 +143,5 @@ export function CurrentAlerts(): VNode {
 }
 
 function Wrapper({ children }: { children: ComponentChildren }): VNode {
-  return <div style={{ margin: "2em" }}>{children}</div>;
+  return <div style={{ margin: "1em" }}>{children}</div>;
 }
diff --git 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
index 2155c7aa6..d54dfe8fc 100644
--- 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
@@ -24,7 +24,7 @@ import {
   Transaction,
   TransactionType,
 } from "@gnu-taler/taler-util";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { PendingTransactionsView as TestedComponent } from 
"./PendingTransactions.js";
 
 export default {
@@ -32,7 +32,7 @@ export default {
   component: TestedComponent,
 };
 
-export const OnePendingTransaction = createExample(TestedComponent, {
+export const OnePendingTransaction = tests.createExample(TestedComponent, {
   transactions: [
     {
       amountEffective: "USD:10",
@@ -42,7 +42,7 @@ export const OnePendingTransaction = 
createExample(TestedComponent, {
   ],
 });
 
-export const ThreePendingTransactions = createExample(TestedComponent, {
+export const ThreePendingTransactions = tests.createExample(TestedComponent, {
   transactions: [
     {
       amountEffective: "USD:10",
@@ -62,7 +62,7 @@ export const ThreePendingTransactions = 
createExample(TestedComponent, {
   ],
 });
 
-export const TenPendingTransactions = createExample(TestedComponent, {
+export const TenPendingTransactions = tests.createExample(TestedComponent, {
   transactions: [
     {
       amountEffective: "USD:10",
diff --git a/packages/taler-wallet-webextension/src/components/QR.stories.tsx 
b/packages/taler-wallet-webextension/src/components/QR.stories.tsx
index 83365670e..bdaa842f2 100644
--- a/packages/taler-wallet-webextension/src/components/QR.stories.tsx
+++ b/packages/taler-wallet-webextension/src/components/QR.stories.tsx
@@ -19,13 +19,13 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { QR } from "./QR.js";
 
 export default {
   title: "qr",
 };
 
-export const Restore = createExample(QR, {
+export const Restore = tests.createExample(QR, {
   text: 
"taler://restore/6J0RZTJC6AV21WXK87BTE67WTHE9P2QSHF2BZXTP7PDZY2ARYBPG@sync1.demo.taler.net,sync2.demo.taler.net,sync1.demo.taler.net,sync3.demo.taler.net",
 });
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
index 8c94e6e60..ef88d1c28 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
@@ -20,7 +20,7 @@
  */
 
 import { WalletContractData } from "@gnu-taler/taler-wallet-core";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import {
   ErrorView,
   HiddenView,
@@ -86,10 +86,10 @@ const cd: WalletContractData = {
   deliveryLocation: undefined,
 };
 
-export const ShowingSimpleOrder = createExample(ShowView, {
+export const ShowingSimpleOrder = tests.createExample(ShowView, {
   contractTerms: cd,
 });
-export const Error = createExample(ErrorView, {
+export const Error = tests.createExample(ErrorView, {
   proposalId: "asd",
   error: {
     hasError: true,
@@ -103,5 +103,5 @@ export const Error = createExample(ErrorView, {
     // },
   },
 });
-export const Loading = createExample(LoadingView, {});
-export const Hidden = createExample(HiddenView, {});
+export const Loading = tests.createExample(LoadingView, {});
+export const Hidden = tests.createExample(HiddenView, {});
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
index 9871611f2..3e1f1dbe4 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
@@ -24,14 +24,14 @@ import { useState } from "preact/hooks";
 import { Loading } from "../components/Loading.js";
 import { Modal } from "../components/Modal.js";
 import { Time } from "../components/Time.js";
-import { alertFromError } from "../context/alert.js";
+import { alertFromError, useAlertContext } from "../context/alert.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 { Amount } from "./Amount.js";
-import { AlertView } from "./CurrentAlerts.js";
+import { ErrorAlertView } from "./CurrentAlerts.js";
 import { Link } from "./styled/index.js";
 
 const ContractTermsTable = styled.table`
@@ -102,6 +102,7 @@ interface Props {
 function useComponentState({ proposalId }: Props): State {
   const api = useBackendContext();
   const [show, setShow] = useState(false);
+  const { pushAlertOnError } = useAlertContext();
   const hook = useAsyncAsHook(async () => {
     if (!show) return undefined;
     return await api.wallet.call(WalletApiOperation.GetContractTermsDetails, {
@@ -110,10 +111,10 @@ function useComponentState({ proposalId }: Props): State {
   }, [show]);
 
   const hideHandler = {
-    onClick: async () => setShow(false),
+    onClick: pushAlertOnError(async () => setShow(false)),
   };
   const showHandler = {
-    onClick: async () => setShow(true),
+    onClick: pushAlertOnError(async () => setShow(true)),
   };
   if (!show) {
     return {
@@ -161,8 +162,8 @@ export function ErrorView({
   const { i18n } = useTranslationContext();
   return (
     <Modal title="Full detail" onClose={hideHandler}>
-      <AlertView
-        alert={alertFromError(
+      <ErrorAlertView
+        error={alertFromError(
           i18n.str`Could not load purchase proposal details`,
           error,
           { proposalId },
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
index c25c0ed13..541b2d39e 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
@@ -28,7 +28,7 @@ export function useComponentState({ exchangeUrl, onChange }: 
Props): State {
   const readOnly = !onChange;
   const [showContent, setShowContent] = useState<boolean>(readOnly);
   const { i18n } = useTranslationContext();
-  const { pushAlert } = useAlertContext();
+  const { pushAlertOnError } = useAlertContext();
 
   /**
    * For the exchange selected, bring the status of the terms of service
@@ -67,24 +67,20 @@ export function useComponentState({ exchangeUrl, onChange 
}: Props): State {
   async function onUpdate(accepted: boolean): Promise<void> {
     if (!state) return;
 
-    try {
-      if (accepted) {
-        await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
-          exchangeBaseUrl: exchangeUrl,
-          etag: state.version,
-        });
-      } else {
-        // mark as not accepted
-        await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
-          exchangeBaseUrl: exchangeUrl,
-          etag: undefined,
-        });
-      }
-      // setAccepted(accepted);
-      if (!readOnly) onChange(accepted); //external update
-    } catch (e) {
-      pushAlert(alertFromError(i18n.str`Could not accept terms of service`, 
e));
+    if (accepted) {
+      await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
+        exchangeBaseUrl: exchangeUrl,
+        etag: state.version,
+      });
+    } else {
+      // mark as not accepted
+      await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
+        exchangeBaseUrl: exchangeUrl,
+        etag: undefined,
+      });
     }
+    // setAccepted(accepted);
+    if (!readOnly) onChange(accepted); //external update
   }
 
   const accepted = state.status === "accepted";
@@ -94,20 +90,20 @@ export function useComponentState({ exchangeUrl, onChange 
}: Props): State {
     showingTermsOfService: {
       value: showContent,
       button: {
-        onClick: async () => {
+        onClick: pushAlertOnError(async () => {
           setShowContent(!showContent);
-        },
+        }),
       },
     },
     terms: state,
     termsAccepted: {
       value: accepted,
       button: {
-        onClick: async () => {
+        onClick: pushAlertOnError(async () => {
           const newValue = !accepted; //toggle
-          onUpdate(newValue);
+          await onUpdate(newValue);
           setShowContent(false);
-        },
+        }),
       },
     },
   };
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx 
b/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx
index 2479274cb..9ef1c4298 100644
--- 
a/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 // import { ReadyView } from "./views.js";
 
 export default {
   title: "TermsOfService",
 };
 
-// export const Ready = createExample(ReadyView, {});
+// export const Ready = tests.createExample(ReadyView, {});
diff --git a/packages/taler-wallet-webextension/src/context/alert.ts 
b/packages/taler-wallet-webextension/src/context/alert.ts
index cc98ec1e0..e67d94671 100644
--- a/packages/taler-wallet-webextension/src/context/alert.ts
+++ b/packages/taler-wallet-webextension/src/context/alert.ts
@@ -19,19 +19,26 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util";
 import { ComponentChildren, createContext, h, VNode } from "preact";
 import { useContext, useState } from "preact/hooks";
+import { HookError } from "../hooks/useAsyncAsHook.js";
+import { SafeHandler, withSafe } from "../mui/handlers.js";
+import { BackgroundError } from "../wxApi.js";
 
 export type AlertType = "info" | "warning" | "error" | "success";
 
-export interface Alert {
+export interface InfoAlert {
   message: TranslatedString;
   description: TranslatedString | VNode;
-  type: AlertType;
+  type: "info" | "warning" | "success";
 }
 
-export interface ErrorAlert extends Alert {
+export type Alert = InfoAlert | ErrorAlert;
+
+export interface ErrorAlert {
+  message: TranslatedString;
+  description: TranslatedString | VNode;
   type: "error";
   context: object;
   cause: any;
@@ -41,10 +48,14 @@ type Type = {
   alerts: Alert[];
   pushAlert: (n: Alert) => void;
   removeAlert: (n: Alert) => void;
+  pushAlertOnError: <T>(h: (p: T) => Promise<void>) => SafeHandler<T>;
 };
 
 const initial: Type = {
   alerts: [],
+  pushAlertOnError: () => {
+    throw Error("alert context not initialized");
+  },
   pushAlert: () => {
     null;
   },
@@ -80,8 +91,17 @@ export const AlertProvider = ({ children }: Props): VNode => 
{
     setAlerts((ns: AlertWithDate[]) => ns.filter((n) => n !== alert));
   };
 
+  function pushAlertOnError<T>(
+    handler: (p: T) => Promise<void>,
+  ): SafeHandler<T> {
+    return withSafe(handler, (e) => {
+      const a = alertFromError(e.message as TranslatedString, e);
+      pushAlert(a);
+    });
+  }
+
   return h(Context.Provider, {
-    value: { alerts, pushAlert, removeAlert },
+    value: { alerts, pushAlert, removeAlert, pushAlertOnError },
     children,
   });
 };
@@ -90,29 +110,71 @@ export const useAlertContext = (): Type => 
useContext(Context);
 
 export function alertFromError(
   message: TranslatedString,
-  error: unknown,
+  error: HookError,
   ...context: any[]
-): ErrorAlert {
-  let description = "" as TranslatedString;
+): ErrorAlert;
 
-  const isObject = typeof error === "object" &&
-    error !== null;
-  const hasMessage =
-    isObject &&
-    "message" in error &&
-    typeof error.message === "string";
+export function alertFromError(
+  message: TranslatedString,
+  error: Error,
+  ...context: any[]
+): ErrorAlert;
 
-  if (hasMessage) {
-    description = error.message as TranslatedString;
+export function alertFromError(
+  message: TranslatedString,
+  error: TalerErrorDetail,
+  ...context: any[]
+): ErrorAlert;
+
+export function alertFromError(
+  message: TranslatedString,
+  error: HookError | TalerErrorDetail | Error,
+  ...context: any[]
+): ErrorAlert {
+  let description: TranslatedString;
+  let cause: any;
+
+  if (typeof error === "object" && error !== null) {
+    if ("code" in error) {
+      //TalerErrorDetail
+      description = (error.hint ??
+        `Error code: ${error.code}`) as TranslatedString;
+      cause = {
+        details: error,
+      };
+    } else if ("hasError" in error) {
+      //HookError
+      description = error.message as TranslatedString;
+      if (error.type === "taler") {
+        cause = {
+          details: error.details,
+        };
+      }
+    } else {
+      if (error instanceof BackgroundError) {
+        description = (error.errorDetail.hint ??
+          `Error code: ${error.errorDetail.code}`) as TranslatedString;
+        cause = {
+          details: error.errorDetail,
+          stack: error.stack,
+        };
+      } else {
+        description = error.message as TranslatedString;
+        cause = {
+          stack: error.stack,
+        };
+      }
+    }
   } else {
-    description = `Unknown error: ${String(error)}` as TranslatedString;
+    description = "" as TranslatedString;
+    cause = error;
   }
 
   return {
     type: "error",
     message,
     description,
-    cause: error,
+    cause,
     context,
   };
 }
diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts 
b/packages/taler-wallet-webextension/src/context/devContext.ts
index 99301df52..e2ad2914b 100644
--- a/packages/taler-wallet-webextension/src/context/devContext.ts
+++ b/packages/taler-wallet-webextension/src/context/devContext.ts
@@ -22,16 +22,15 @@
 import { createContext, h, VNode } from "preact";
 import { useContext } from "preact/hooks";
 import { useWalletDevMode } from "../hooks/useWalletDevMode.js";
-import { ToggleHandler } from "../mui/handlers.js";
 
 interface Type {
   devMode: boolean;
-  devModeToggle: ToggleHandler;
+  toggle: () => Promise<void>;
 }
 const Context = createContext<Type>({
   devMode: false,
-  devModeToggle: {
-    button: {},
+  toggle: async () => {
+    null;
   },
 });
 
@@ -47,9 +46,8 @@ export const DevContextProviderForTesting = ({
   return h(Context.Provider, {
     value: {
       devMode: !!value,
-      devModeToggle: {
-        value,
-        button: {},
+      toggle: async () => {
+        null;
       },
     },
     children,
@@ -58,7 +56,10 @@ export const DevContextProviderForTesting = ({
 
 export const DevContextProvider = ({ children }: { children: any }): VNode => {
   const devModeToggle = useWalletDevMode();
-  const value: Type = { devMode: !!devModeToggle.value, devModeToggle };
+  const value: Type = {
+    devMode: !!devModeToggle.value,
+    toggle: devModeToggle.toggle,
+  };
   //support for function as children, useful for getting the value right away
   children =
     children.length === 1 && typeof children === "function"
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
index 4cee7cfd0..3e09597a2 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
@@ -16,7 +16,7 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -29,6 +29,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const info = useAsyncAsHook(async () => {
     if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
     if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");
@@ -66,7 +67,7 @@ export function useComponentState({
     status: "ready",
     error: undefined,
     confirm: {
-      onClick: doDeposit,
+      onClick: pushAlertOnError(doDeposit),
     },
     fee: Amounts.sub(deposit.totalDepositCost, deposit.effectiveDepositAmount)
       .amount,
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
index 6d1535953..fd3044dcb 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx
@@ -20,14 +20,14 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "deposit",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   status: "ready",
   confirm: {},
   cost: Amounts.parseOrThrow("EUR:1.2"),
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
index 031dcffaa..b9fbc3638 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
@@ -55,7 +55,7 @@ describe("Deposit CTA states", () => {
           if (!error) expect.fail();
           // if (!error.hasError) expect.fail();
           // if (error.operational) expect.fail();
-          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
+          expect(error.description).eq("ERROR_NO-URI-FOR-DEPOSIT");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index 7dcda4c52..ee5375859 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -15,15 +15,11 @@
  */
 
 /* eslint-disable react-hooks/rules-of-hooks */
-import {
-  Amounts,
-  TalerErrorDetail,
-  TalerProtocolTimestamp,
-} from "@gnu-taler/taler-util";
+import { Amounts, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -71,6 +67,7 @@ export function useComponentState({
   return () => {
     const [subject, setSubject] = useState<string | undefined>();
     const [timestamp, setTimestamp] = useState<string | undefined>();
+    const { pushAlertOnError } = useAlertContext();
 
     const selectedExchange = useSelectedExchange({
       currency: amount.currency,
@@ -144,27 +141,20 @@ export function useComponentState({
 
     async function accept(): Promise<void> {
       if (!subject || !purse_expiration) return;
-      try {
-        const resp = await api.wallet.call(
-          WalletApiOperation.InitiatePeerPullPayment,
-          {
-            exchangeBaseUrl: exchange.exchangeBaseUrl,
-            partialContractTerms: {
-              amount: Amounts.stringify(amount),
-              summary: subject,
-              purse_expiration,
-            },
+
+      const resp = await api.wallet.call(
+        WalletApiOperation.InitiatePeerPullPayment,
+        {
+          exchangeBaseUrl: exchange.exchangeBaseUrl,
+          partialContractTerms: {
+            amount: Amounts.stringify(amount),
+            summary: subject,
+            purse_expiration,
           },
-        );
+        },
+      );
 
-        onSuccess(resp.transactionId);
-      } catch (e) {
-        if (e instanceof TalerError) {
-          // setOperationError(e.errorDetail);
-        }
-        console.error(e);
-        throw Error("error trying to accept");
-      }
+      onSuccess(resp.transactionId);
     }
     const unableToCreate =
       !subject || Amounts.isZero(amount) || !purse_expiration;
@@ -176,25 +166,25 @@ export function useComponentState({
           subject === undefined
             ? undefined
             : !subject
-              ? "Can't be empty"
-              : undefined,
+            ? "Can't be empty"
+            : undefined,
         value: subject ?? "",
-        onInput: async (e) => setSubject(e),
+        onInput: pushAlertOnError(async (e) => setSubject(e)),
       },
       expiration: {
         error: timestampError,
         value: timestamp === undefined ? "" : timestamp,
-        onInput: async (e) => {
+        onInput: pushAlertOnError(async (e) => {
           setTimestamp(e);
-        },
+        }),
       },
       doSelectExchange: selectedExchange.doSelect,
       exchangeUrl: exchange.exchangeBaseUrl,
       create: {
-        onClick: unableToCreate ? undefined : accept,
+        onClick: unableToCreate ? undefined : pushAlertOnError(accept),
       },
       cancel: {
-        onClick: onClose,
+        onClick: pushAlertOnError(onClose),
       },
       requestAmount,
       toBeReceived,
diff --git 
a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
index 05b923c9e..4ab4dc8f6 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx
@@ -19,14 +19,15 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { nullFunction } from "../../mui/handlers.js";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "invoice create",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   requestAmount: {
     currency: "ARS",
     value: 1,
@@ -45,9 +46,7 @@ export const Ready = createExample(ReadyView, {
   exchangeUrl: "https://exchange.taler.ar";,
   subject: {
     value: "some subject",
-    onInput: async () => {
-      null;
-    },
+    onInput: nullFunction,
   },
   create: {},
 });
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index 82b2c7af5..c8a7eed65 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -61,7 +61,6 @@ export namespace State {
     goToWalletManualWithdraw: (currency: string) => Promise<void>;
     summary: string | undefined;
     expiration: AbsoluteTime | undefined;
-    operationError?: TalerErrorDetail;
     payStatus: PreparePayResult;
   }
 
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index 9c4a3162e..66c018ddf 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -25,10 +25,11 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
+import { withSafe } from "../../mui/handlers.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({
@@ -39,6 +40,7 @@ export function useComponentState({
 }: Props): State {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
+  const { pushAlertOnError } = useAlertContext();
   const hook = useAsyncAsHook(async () => {
     const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, 
{
       talerUri: talerPayPullUri,
@@ -54,10 +56,6 @@ export function useComponentState({
     ),
   );
 
-  const [operationError, setOperationError] = useState<
-    TalerErrorDetail | undefined
-  >(undefined);
-
   if (!hook) {
     return {
       status: "loading",
@@ -109,18 +107,17 @@ export function useComponentState({
     contractTerms: {} as any,
     amountRaw: hook.response.p2p.amount,
     noncePriv: "",
-  };
+  } as any; //FIXME: check this interface with new values
 
   const baseResult = {
     uri: talerPayPullUri,
     cancel: {
-      onClick: onClose,
+      onClick: pushAlertOnError(onClose),
     },
     amount,
     goToWalletManualWithdraw,
     summary,
     expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : 
undefined,
-    operationError,
   };
 
   if (!foundBalance) {
@@ -148,21 +145,13 @@ export function useComponentState({
   }
 
   async function accept(): Promise<void> {
-    try {
-      const resp = await api.wallet.call(
-        WalletApiOperation.AcceptPeerPullPayment,
-        {
-          peerPullPaymentIncomingId,
-        },
-      );
-      onSuccess(resp.transactionId);
-    } catch (e) {
-      if (e instanceof TalerError) {
-        setOperationError(e.errorDetail);
-      }
-      console.error(e);
-      throw Error("error trying to accept");
-    }
+    const resp = await api.wallet.call(
+      WalletApiOperation.AcceptPeerPullPayment,
+      {
+        peerPullPaymentIncomingId,
+      },
+    );
+    onSuccess(resp.transactionId);
   }
 
   return {
@@ -172,7 +161,7 @@ export function useComponentState({
     payStatus: paymentPossible,
     balance: foundAmount,
     accept: {
-      onClick: accept,
+      onClick: pushAlertOnError(accept),
     },
   };
 }
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
index 749cd78fc..1dada5a91 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
@@ -20,14 +20,14 @@
  */
 
 import { PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "invoice payment",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   amount: {
     currency: "ARS",
     value: 1,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
index 6a9ab3cf7..9a748891c 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
@@ -16,11 +16,10 @@
 
 import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
-import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
 import { PaymentButtons } from "../../components/PaymentButtons.js";
-import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
+import { SubTitle, WalletAction } from "../../components/styled/index.js";
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { State } from "./index.js";
@@ -29,29 +28,14 @@ export function ReadyView(
   state: State.Ready | State.NoBalanceForCurrency | State.NoEnoughBalance,
 ): VNode {
   const { i18n } = useTranslationContext();
-  const {
-    operationError,
-    summary,
-    amount,
-    expiration,
-    uri,
-    status,
-    balance,
-    payStatus,
-    cancel,
-  } = state;
+  const { summary, amount, expiration, uri, status, balance, payStatus } =
+    state;
   return (
     <WalletAction>
       <LogoHeader />
       <SubTitle>
         <i18n.Translate>Digital invoice</i18n.Translate>
       </SubTitle>
-      {operationError && (
-        <ErrorTalerOperation
-          title={i18n.str`Could not finish the payment operation`}
-          error={operationError}
-        />
-      )}
       <section style={{ textAlign: "left" }}>
         <Part title={i18n.str`Subject`} text={<div>{summary}</div>} />
         <Part title={i18n.str`Amount`} text={<Amount value={amount} />} />
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
index 6d7ef6b20..0f1388ea5 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
@@ -19,11 +19,10 @@ import {
   ConfirmPayResultType,
   NotificationType,
   PreparePayResultType,
-  TalerErrorCode,
 } from "@gnu-taler/taler-util";
-import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { useEffect, useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useEffect } from "preact/hooks";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -36,7 +35,7 @@ export function useComponentState({
   goToWalletManualWithdraw,
   onSuccess,
 }: Props): State {
-  const [payErrMsg, setPayErrMsg] = useState<TalerError | 
undefined>(undefined);
+  const { pushAlertOnError } = useAlertContext();
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
 
@@ -142,43 +141,41 @@ export function useComponentState({
   }
 
   async function doPayment(): Promise<void> {
-    try {
-      if (payStatus.status !== "payment-possible") {
-        throw TalerError.fromUncheckedDetail({
-          code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR,
-          hint: `payment is not possible: ${payStatus.status}`,
-        });
-      }
-      const res = await api.wallet.call(WalletApiOperation.ConfirmPay, {
-        proposalId: payStatus.proposalId,
-      });
-      // handle confirm pay
-      if (res.type !== ConfirmPayResultType.Done) {
-        throw TalerError.fromUncheckedDetail({
-          code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR,
-          hint: `could not confirm payment`,
-          payResult: res,
-        });
-      }
-      const fu = res.contractTerms.fulfillment_url;
-      if (fu) {
-        if (typeof window !== "undefined") {
-          document.location.href = fu;
-        } else {
-          console.log(`should d to ${fu}`);
-        }
-      }
+    // if (payStatus.status !== "payment-possible") {
+    //   throw TalerError.fromUncheckedDetail({
+    //     code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR,
+    //     when: new Date().toISOString(),
+    //     hint: `payment is not possible: ${payStatus.status}`,
+    //   });
+    // }
+    const res = await api.wallet.call(WalletApiOperation.ConfirmPay, {
+      proposalId: payStatus.proposalId,
+    });
+    // handle confirm pay
+    if (res.type !== ConfirmPayResultType.Done) {
+      // throw new BackgroundError("Could not confirm payment", res.lastError)
+      // // throw TalerError.fromUncheckedDetail({
+      // //   code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR,
+      // //   when: new Date().toISOString(),
+      // //   hint: `could not confirm payment`,
+      // //   payResult: res,
+      // // });
       onSuccess(res.transactionId);
-    } catch (e) {
-      if (e instanceof TalerError) {
-        setPayErrMsg(e);
+      return;
+    }
+    const fu = res.contractTerms.fulfillment_url;
+    if (fu) {
+      if (typeof window !== "undefined") {
+        document.location.href = fu;
+      } else {
+        console.log(`should d to ${fu}`);
       }
     }
+    onSuccess(res.transactionId);
   }
 
   const payHandler: ButtonHandler = {
-    onClick: payErrMsg ? undefined : doPayment,
-    error: payErrMsg,
+    onClick: pushAlertOnError(doPayment),
   };
 
   // (payStatus.status === PreparePayResultType.PaymentPossible)
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx
index 28fcd8db7..b63190236 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx
@@ -24,10 +24,11 @@ import {
   MerchantContractTerms as ContractTerms,
   PreparePayResultType,
 } from "@gnu-taler/taler-util";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import beer from "../../../static-dev/beer.png";
 import merchantIcon from "../../../static-dev/merchant-icon.jpeg";
-import { createExample } from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
 import { BaseView } from "./views.js";
-import beer from "../../../static-dev/beer.png";
 
 export default {
   title: "payment",
@@ -35,7 +36,7 @@ export default {
   argTypes: {},
 };
 
-export const NoBalance = createExample(BaseView, {
+export const NoBalance = tests.createExample(BaseView, {
   status: "no-balance-for-currency",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -44,6 +45,7 @@ export const NoBalance = createExample(BaseView, {
   uri: "",
   payStatus: {
     status: PreparePayResultType.InsufficientBalance,
+    balanceDetails: {} as any,
     talerUri: "taler://pay/..",
     noncePriv: "",
     proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0",
@@ -61,7 +63,7 @@ export const NoBalance = createExample(BaseView, {
   },
 });
 
-export const NoEnoughBalance = createExample(BaseView, {
+export const NoEnoughBalance = tests.createExample(BaseView, {
   status: "no-enough-balance",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -74,6 +76,7 @@ export const NoEnoughBalance = createExample(BaseView, {
   uri: "",
   payStatus: {
     status: PreparePayResultType.InsufficientBalance,
+    balanceDetails: {} as any,
     talerUri: "taler://pay/..",
     noncePriv: "",
     proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0",
@@ -91,7 +94,7 @@ export const NoEnoughBalance = createExample(BaseView, {
   },
 });
 
-export const EnoughBalanceButRestricted = createExample(BaseView, {
+export const EnoughBalanceButRestricted = tests.createExample(BaseView, {
   status: "no-enough-balance",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -104,6 +107,7 @@ export const EnoughBalanceButRestricted = 
createExample(BaseView, {
   uri: "",
   payStatus: {
     status: PreparePayResultType.InsufficientBalance,
+    balanceDetails: {} as any,
     talerUri: "taler://pay/..",
     noncePriv: "",
     proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0",
@@ -121,7 +125,7 @@ export const EnoughBalanceButRestricted = 
createExample(BaseView, {
   },
 });
 
-export const PaymentPossible = createExample(BaseView, {
+export const PaymentPossible = tests.createExample(BaseView, {
   status: "ready",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -131,9 +135,7 @@ export const PaymentPossible = createExample(BaseView, {
     value: 11,
   },
   payHandler: {
-    onClick: async () => {
-      null;
-    },
+    onClick: nullFunction,
   },
 
   uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
@@ -162,7 +164,7 @@ export const PaymentPossible = createExample(BaseView, {
   },
 });
 
-export const PaymentPossibleWithFee = createExample(BaseView, {
+export const PaymentPossibleWithFee = tests.createExample(BaseView, {
   status: "ready",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -172,9 +174,7 @@ export const PaymentPossibleWithFee = 
createExample(BaseView, {
     value: 11,
   },
   payHandler: {
-    onClick: async () => {
-      null;
-    },
+    onClick: nullFunction,
   },
 
   uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
@@ -200,7 +200,7 @@ export const PaymentPossibleWithFee = 
createExample(BaseView, {
   },
 });
 
-export const TicketWithAProductList = createExample(BaseView, {
+export const TicketWithAProductList = tests.createExample(BaseView, {
   status: "ready",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -210,9 +210,7 @@ export const TicketWithAProductList = 
createExample(BaseView, {
     value: 11,
   },
   payHandler: {
-    onClick: async () => {
-      null;
-    },
+    onClick: nullFunction,
   },
 
   uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
@@ -257,7 +255,7 @@ export const TicketWithAProductList = 
createExample(BaseView, {
   },
 });
 
-export const TicketWithShipping = createExample(BaseView, {
+export const TicketWithShipping = tests.createExample(BaseView, {
   status: "ready",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
@@ -267,9 +265,7 @@ export const TicketWithShipping = createExample(BaseView, {
     value: 11,
   },
   payHandler: {
-    onClick: async () => {
-      null;
-    },
+    onClick: nullFunction,
   },
 
   uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
@@ -309,7 +305,7 @@ export const TicketWithShipping = createExample(BaseView, {
   },
 });
 
-export const AlreadyConfirmedByOther = createExample(BaseView, {
+export const AlreadyConfirmedByOther = tests.createExample(BaseView, {
   status: "confirmed",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:10"),
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
index 123e95a87..f53be00c9 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
@@ -31,7 +31,8 @@ import {
 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 { ErrorAlert, useAlertContext } from "../../context/alert.js";
+import { nullFunction } from "../../mui/handlers.js";
 import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
@@ -385,8 +386,12 @@ describe("Payment CTA states", () => {
     } as ConfirmPayResult);
 
     const hookBehavior = await tests.hookBehaveLikeThis(
-      useComponentState,
-      props,
+      () => {
+        const state = useComponentState(props);
+        // const { alerts } = useAlertContext();
+        return { ...state, alerts: {} };
+      },
+      {},
       [
         ({ status, error }) => {
           expect(status).equals("loading");
@@ -400,22 +405,21 @@ describe("Payment CTA states", () => {
           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 },
-          });
-        },
+        // (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"));
+
+        //   // FIXME: check that the error is pushed to the alertContext
+        //   // expect(state.alerts.length).eq(1);
+        //   // const alert = state.alerts[0]
+        //   // if (alert.type !== "error") expect.fail();
+
+        //   // expect(alert.cause.errorDetail.payResult).deep.equal({
+        //   //   type: ConfirmPayResultType.Pending,
+        //   //   lastError: { code: 1 },
+        //   // });
+        // },
       ],
       TestingContext,
     );
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
index 078e53bf9..9731d3f69 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
@@ -16,7 +16,7 @@
 
 import { parseRecoveryUri } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { Alert } from "../../context/alert.js";
+import { useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Props, State } from "./index.js";
@@ -27,6 +27,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const { i18n } = useTranslationContext();
   if (!talerRecoveryUri) {
     return {
@@ -67,10 +68,10 @@ export function useComponentState({
     status: "ready",
 
     accept: {
-      onClick: recoverBackup,
+      onClick: pushAlertOnError(recoverBackup),
     },
     cancel: {
-      onClick: onCancel,
+      onClick: pushAlertOnError(onCancel),
     },
     error: undefined,
   };
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx
index 9243cc015..4f7a14c6d 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx
@@ -20,7 +20,7 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/state.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
index 5a5073ba3..4c411ec04 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
@@ -17,7 +17,7 @@
 import { Amounts, NotificationType } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -31,6 +31,7 @@ export function useComponentState({
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
   const [ignored, setIgnored] = useState(false);
+  const { pushAlertOnError } = useAlertContext();
 
   const info = useAsyncAsHook(async () => {
     if (!talerRefundUri) throw Error("ERROR_NO-URI-FOR-REFUND");
@@ -108,10 +109,10 @@ export function useComponentState({
     ...baseInfo,
     orderId: info.response.refund.info.orderId,
     accept: {
-      onClick: doAccept,
+      onClick: pushAlertOnError(doAccept),
     },
     ignore: {
-      onClick: doIgnore,
+      onClick: pushAlertOnError(doIgnore),
     },
     cancel,
   };
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
index 921cf77e6..faaee1104 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
@@ -21,13 +21,13 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import beer from "../../../static-dev/beer.png";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { IgnoredView, InProgressView, ReadyView } from "./views.js";
 export default {
   title: "refund",
 };
 
-export const InProgress = createExample(InProgressView, {
+export const InProgress = tests.createExample(InProgressView, {
   status: "in-progress",
   error: undefined,
   amount: Amounts.parseOrThrow("USD:1"),
@@ -37,7 +37,7 @@ export const InProgress = createExample(InProgressView, {
   products: undefined,
 });
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   status: "ready",
   error: undefined,
   accept: {},
@@ -51,7 +51,7 @@ export const Ready = createExample(ReadyView, {
   orderId: "abcdef",
 });
 
-export const WithAProductList = createExample(ReadyView, {
+export const WithAProductList = tests.createExample(ReadyView, {
   status: "ready",
   error: undefined,
   accept: {},
@@ -75,7 +75,7 @@ export const WithAProductList = createExample(ReadyView, {
   orderId: "abcdef",
 });
 
-export const Ignored = createExample(IgnoredView, {
+export const Ignored = tests.createExample(IgnoredView, {
   status: "ignored",
   error: undefined,
   merchantName: "the merchant",
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/test.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
index 8c4daa4d2..a07158e1a 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
@@ -27,11 +27,8 @@ import {
 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,
-} from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 describe("Refund CTA states", () => {
@@ -57,7 +54,7 @@ describe("Refund CTA states", () => {
           if (!error) expect.fail();
           // if (!error.hasError) expect.fail();
           // if (error.operational) expect.fail();
-          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-REFUND");
+          expect(error.description).eq("ERROR_NO-URI-FOR-REFUND");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/state.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
index 29a9c4c71..3b9abf5a3 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
@@ -16,7 +16,7 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -29,6 +29,7 @@ export function useComponentState({
 }: Props): State {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
+  const { pushAlertOnError } = useAlertContext();
   const tipInfo = useAsyncAsHook(async () => {
     if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
     const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
@@ -77,7 +78,7 @@ export function useComponentState({
     amount: Amounts.parseOrThrow(tip.tipAmountEffective),
     error: undefined,
     cancel: {
-      onClick: onCancel,
+      onClick: pushAlertOnError(onCancel),
     },
   };
 
@@ -92,7 +93,7 @@ export function useComponentState({
     status: "ready",
     ...baseInfo,
     accept: {
-      onClick: doAccept,
+      onClick: pushAlertOnError(doAccept),
     },
   };
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx
index 86bdd27a9..dd358d9d2 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx
@@ -20,14 +20,14 @@
  */
 
 import { Amounts } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { AcceptedView, ReadyView } from "./views.js";
 
 export default {
   title: "tip",
 };
 
-export const Accepted = createExample(AcceptedView, {
+export const Accepted = tests.createExample(AcceptedView, {
   status: "accepted",
   error: undefined,
   amount: Amounts.parseOrThrow("EUR:1"),
@@ -35,7 +35,7 @@ export const Accepted = createExample(AcceptedView, {
   merchantBaseUrl: "",
 });
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   status: "ready",
   error: undefined,
   amount: Amounts.parseOrThrow("EUR:1"),
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/test.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
index 2cc95f424..44a6f9b0b 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
@@ -23,7 +23,8 @@ import { Amounts } 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, nullFunction } from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
 import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
 
@@ -48,7 +49,7 @@ describe("Tip CTA states", () => {
         ({ status, error }) => {
           expect(status).equals("error");
           if (!error) expect.fail();
-          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-TIP");
+          expect(error.description).eq("ERROR_NO-URI-FOR-TIP");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
index b191b4efa..654b03b7f 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
@@ -54,7 +54,6 @@ export namespace State {
     subject: TextFieldHandler;
     expiration: TextFieldHandler;
     error: undefined;
-    operationError?: TalerErrorDetail;
   }
 }
 
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index ecea53848..6574d6ba1 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -22,7 +22,7 @@ import {
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -34,16 +34,13 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const amount = Amounts.parseOrThrow(amountStr);
   const { i18n } = useTranslationContext();
 
   const [subject, setSubject] = useState<string | undefined>();
   const [timestamp, setTimestamp] = useState<string | undefined>();
 
-  const [operationError, setOperationError] = useState<
-    TalerErrorDetail | undefined
-  >(undefined);
-
   const hook = useAsyncAsHook(async () => {
     const resp = await api.wallet.call(
       WalletApiOperation.PreparePeerPushPayment,
@@ -104,25 +101,17 @@ export function useComponentState({
 
   async function accept(): Promise<void> {
     if (!subject || !purse_expiration) return;
-    try {
-      const resp = await api.wallet.call(
-        WalletApiOperation.InitiatePeerPushPayment,
-        {
-          partialContractTerms: {
-            summary: subject,
-            amount: amountStr,
-            purse_expiration,
-          },
+    const resp = await api.wallet.call(
+      WalletApiOperation.InitiatePeerPushPayment,
+      {
+        partialContractTerms: {
+          summary: subject,
+          amount: amountStr,
+          purse_expiration,
         },
-      );
-      onSuccess(resp.transactionId);
-    } catch (e) {
-      if (e instanceof TalerError) {
-        setOperationError(e.errorDetail);
-      }
-      console.error(e);
-      throw Error("error trying to accept");
-    }
+      },
+    );
+    onSuccess(resp.transactionId);
   }
 
   const unableToCreate =
@@ -131,7 +120,7 @@ export function useComponentState({
   return {
     status: "ready",
     cancel: {
-      onClick: onClose,
+      onClick: pushAlertOnError(onClose),
     },
     subject: {
       error:
@@ -141,21 +130,20 @@ export function useComponentState({
           ? "Can't be empty"
           : undefined,
       value: subject ?? "",
-      onInput: async (e) => setSubject(e),
+      onInput: pushAlertOnError(async (e) => setSubject(e)),
     },
     expiration: {
       error: timestampError,
       value: timestamp === undefined ? "" : timestamp,
-      onInput: async (e) => {
+      onInput: pushAlertOnError(async (e) => {
         setTimestamp(e);
-      },
+      }),
     },
     create: {
-      onClick: unableToCreate ? undefined : accept,
+      onClick: unableToCreate ? undefined : pushAlertOnError(accept),
     },
     debitAmount,
     toBeReceived,
     error: undefined,
-    operationError,
   };
 }
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
index d0650f562..57409bde5 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx
@@ -19,14 +19,15 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { nullFunction } from "../../mui/handlers.js";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "transfer create",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   debitAmount: {
     currency: "ARS",
     value: 1,
@@ -44,8 +45,6 @@ export const Ready = createExample(ReadyView, {
   },
   subject: {
     value: "the subject",
-    onInput: async () => {
-      null;
-    },
+    onInput: nullFunction,
   },
 });
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
index cee61b3b8..373af8f74 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
@@ -32,8 +32,6 @@ export function ReadyView({
   toBeReceived,
   debitAmount,
   create,
-  operationError,
-  cancel,
 }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
 
@@ -65,12 +63,6 @@ export function ReadyView({
       <SubTitle>
         <i18n.Translate>Digital cash transfer</i18n.Translate>
       </SubTitle>
-      {operationError && (
-        <ErrorTalerOperation
-          title={i18n.str`Could not finish the transfer creation`}
-          error={operationError}
-        />
-      )}
       <section style={{ textAlign: "left" }}>
         <p>
           <TextField
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
index 7bb8785d7..5cfbe7170 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
@@ -57,7 +57,6 @@ export namespace State {
     expiration: AbsoluteTime | undefined;
     error: undefined;
     accept: ButtonHandler;
-    operationError?: TalerErrorDetail;
   }
 }
 
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
index 04fc0e0a7..12643b893 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
@@ -22,7 +22,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -34,15 +34,13 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
       talerUri: talerPayPushUri,
     });
   }, []);
-  const [operationError, setOperationError] = useState<
-    TalerErrorDetail | undefined
-  >(undefined);
 
   if (!hook) {
     return {
@@ -74,34 +72,25 @@ export function useComponentState({
     contractTerms?.purse_expiration;
 
   async function accept(): Promise<void> {
-    try {
-      const resp = await api.wallet.call(
-        WalletApiOperation.AcceptPeerPushPayment,
-        {
-          peerPushPaymentIncomingId,
-        },
-      );
-      onSuccess(resp.transactionId);
-    } catch (e) {
-      if (e instanceof TalerError) {
-        setOperationError(e.errorDetail);
-      }
-      console.error(e);
-      throw Error("error trying to accept");
-    }
+    const resp = await api.wallet.call(
+      WalletApiOperation.AcceptPeerPushPayment,
+      {
+        peerPushPaymentIncomingId,
+      },
+    );
+    onSuccess(resp.transactionId);
   }
   return {
     status: "ready",
     amount: Amounts.parseOrThrow(amount),
     error: undefined,
     accept: {
-      onClick: accept,
+      onClick: pushAlertOnError(accept),
     },
     summary,
     expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : 
undefined,
     cancel: {
-      onClick: onClose,
+      onClick: pushAlertOnError(onClose),
     },
-    operationError,
   };
 }
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx
index 250e99ae1..48f006127 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx
@@ -19,14 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "transfer pickup",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   amount: {
     currency: "ARS",
     value: 1,
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
index d2402db3a..25f5cdf52 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
@@ -30,8 +30,6 @@ export function ReadyView({
   summary,
   expiration,
   amount,
-  cancel,
-  operationError,
 }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   return (
@@ -40,12 +38,6 @@ export function ReadyView({
       <SubTitle>
         <i18n.Translate>Digital cash transfer</i18n.Translate>
       </SubTitle>
-      {operationError && (
-        <ErrorTalerOperation
-          title={i18n.str`Could not finish the pickup operation`}
-          error={operationError}
-        />
-      )}
       <section style={{ textAlign: "left" }}>
         <Part title={i18n.str`Subject`} text={<div>{summary}</div>} />
         <Part title={i18n.str`Amount`} text={<Amount value={amount} />} />
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 18c467aae..5f149064c 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -23,7 +23,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -205,6 +205,7 @@ function exchangeSelectionState(
 
   return () => {
     const { i18n } = useTranslationContext();
+    const { pushAlertOnError } = useAlertContext();
     const [ageRestricted, setAgeRestricted] = useState(0);
     const currentExchange = selectedExchange.selected;
     const tosNeedToBeAccepted =
@@ -299,7 +300,9 @@ function exchangeSelectionState(
       ? {
           list: ageRestrictionOptions,
           value: String(ageRestricted),
-          onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
+          onChange: pushAlertOnError(async (v: string) =>
+            setAgeRestricted(parseInt(v, 10)),
+          ),
         }
       : undefined;
 
@@ -317,7 +320,7 @@ function exchangeSelectionState(
         onClick:
           doingWithdraw || tosNeedToBeAccepted
             ? undefined
-            : doWithdrawAndCheckError,
+            : pushAlertOnError(doWithdrawAndCheckError),
         error: withdrawError,
       },
       onTosUpdate,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
index a8031223b..cde03dd8f 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
@@ -20,7 +20,8 @@
  */
 
 import { ExchangeListItem } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { nullFunction } from "../../mui/handlers.js";
 // import { TermsState } from "../../utils/index.js";
 import { SuccessView } from "./views.js";
 
@@ -28,28 +29,6 @@ export default {
   title: "withdraw",
 };
 
-const exchangeList = {
-  "exchange.demo.taler.net": "http://exchange.demo.taler.net (USD)",
-  "exchange.test.taler.net": "http://exchange.test.taler.net (KUDOS)",
-};
-
-const nullHandler = {
-  onClick: async (): Promise<void> => {
-    null;
-  },
-};
-
-// const normalTosState = {
-//   terms: {
-//     status: "accepted",
-//     version: "",
-//   } as TermsState,
-//   onAccept: () => null,
-//   onReview: () => null,
-//   reviewed: false,
-//   reviewing: false,
-// };
-
 const ageRestrictionOptions: Record<string, string> = "6:12:18"
   .split(":")
   .reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {});
@@ -61,7 +40,7 @@ const ageRestrictionSelectField = {
   value: "0",
 };
 
-export const TermsOfServiceNotYetLoaded = createExample(SuccessView, {
+export const TermsOfServiceNotYetLoaded = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   chosenAmount: {
@@ -69,7 +48,7 @@ export const TermsOfServiceNotYetLoaded = 
createExample(SuccessView, {
     value: 2,
     fraction: 10000000,
   },
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
@@ -87,7 +66,7 @@ export const TermsOfServiceNotYetLoaded = 
createExample(SuccessView, {
   },
 });
 
-export const WithSomeFee = createExample(SuccessView, {
+export const WithSomeFee = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   chosenAmount: {
@@ -95,7 +74,7 @@ export const WithSomeFee = createExample(SuccessView, {
     value: 2,
     fraction: 10000000,
   },
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
@@ -113,7 +92,7 @@ export const WithSomeFee = createExample(SuccessView, {
   doSelectExchange: {},
 });
 
-export const WithoutFee = createExample(SuccessView, {
+export const WithoutFee = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   chosenAmount: {
@@ -121,7 +100,7 @@ export const WithoutFee = createExample(SuccessView, {
     value: 2,
     fraction: 0,
   },
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
@@ -139,7 +118,7 @@ export const WithoutFee = createExample(SuccessView, {
   },
 });
 
-export const EditExchangeUntouched = createExample(SuccessView, {
+export const EditExchangeUntouched = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   chosenAmount: {
@@ -147,7 +126,7 @@ export const EditExchangeUntouched = 
createExample(SuccessView, {
     value: 2,
     fraction: 10000000,
   },
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
@@ -165,7 +144,7 @@ export const EditExchangeUntouched = 
createExample(SuccessView, {
   },
 });
 
-export const EditExchangeModified = createExample(SuccessView, {
+export const EditExchangeModified = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   chosenAmount: {
@@ -173,7 +152,7 @@ export const EditExchangeModified = 
createExample(SuccessView, {
     value: 2,
     fraction: 10000000,
   },
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
@@ -191,7 +170,7 @@ export const EditExchangeModified = 
createExample(SuccessView, {
   },
 });
 
-export const WithAgeRestriction = createExample(SuccessView, {
+export const WithAgeRestriction = tests.createExample(SuccessView, {
   error: undefined,
   status: "success",
   ageRestriction: ageRestrictionSelectField,
@@ -201,7 +180,7 @@ export const WithAgeRestriction = 
createExample(SuccessView, {
     fraction: 10000000,
   },
   doSelectExchange: {},
-  doWithdrawal: nullHandler,
+  doWithdrawal: { onClick: nullFunction },
   currentExchange: {
     exchangeBaseUrl: "https://exchange.demo.taler.net";,
     tos: {},
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index 2caa50dca..5a6200844 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -28,7 +28,6 @@ import {
 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";
 
@@ -88,7 +87,7 @@ describe("Withdraw CTA states", () => {
           if (!error) expect.fail();
           // if (!error.hasError) expect.fail();
           // if (error.operational) expect.fail();
-          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
+          expect(error.description).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index cf87b35bb..1cc87547e 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -18,7 +18,6 @@ import { ExchangeTosStatus } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Amount } from "../../components/Amount.js";
-import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
 import { Part } from "../../components/Part.js";
 import { QR } from "../../components/QR.js";
 import { SelectList } from "../../components/SelectList.js";
@@ -36,13 +35,6 @@ export function SuccessView(state: State.Success): VNode {
     state.currentExchange.tosStatus === ExchangeTosStatus.Accepted;
   return (
     <Fragment>
-      {state.doWithdrawal.error && (
-        <ErrorTalerOperation
-          title={i18n.str`Could not finish the withdrawal operation`}
-          error={state.doWithdrawal.error.errorDetail}
-        />
-      )}
-
       <section style={{ textAlign: "left" }}>
         <Part
           title={
diff --git a/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts 
b/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
index 978ea90e1..cf9409bad 100644
--- a/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
@@ -16,7 +16,7 @@
 import { TalerErrorDetail } from "@gnu-taler/taler-util";
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useMemo, useState } from "preact/hooks";
-import { WalletError } from "../wxApi.js";
+import { BackgroundError } from "../wxApi.js";
 
 export interface HookOk<T> {
   hasError: false;
@@ -74,12 +74,12 @@ export function useAsyncAsHook<T>(
           message: e.message,
           details: e.errorDetail,
         });
-      } else if (e instanceof WalletError) {
+      } else if (e instanceof BackgroundError) {
         setHookResponse({
           hasError: true,
           type: "taler",
           message: e.message,
-          details: e.errorDetail.errorDetail,
+          details: e.errorDetail,
         });
       } else if (e instanceof Error) {
         setHookResponse({
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts 
b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
index cf2fd880e..e0a34f690 100644
--- a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts
@@ -14,23 +14,41 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useAlertContext } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { platform } from "../platform/foreground.js";
 
 export function useAutoOpenPermissions(): ToggleHandler {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const [enabled, setEnabled] = useState(false);
-  const [error, setError] = useState<TalerError | undefined>();
-  const toggle = async (): Promise<void> => {
-    return handleAutoOpenPerm(enabled, setEnabled, api.background).catch(
-      (e) => {
-        setError(TalerError.fromException(e));
-      },
-    );
-  };
+
+  async function handleAutoOpenPerm(): Promise<void> {
+    if (!enabled) {
+      // We set permissions here, since apparently FF wants this to be done
+      // as the result of an input event ...
+      let granted: boolean;
+      try {
+        granted = await platform.getPermissionsApi().requestHostPermissions();
+      } catch (lastError) {
+        setEnabled(false);
+        throw lastError;
+      }
+      const res = await api.background.call("toggleHeaderListener", granted);
+      setEnabled(res.newValue);
+    } else {
+      try {
+        await api.background
+          .call("toggleHeaderListener", false)
+          .then((r) => setEnabled(r.newValue));
+      } catch (e) {
+        console.log(e);
+      }
+    }
+    return;
+  }
 
   useEffect(() => {
     async function getValue(): Promise<void> {
@@ -42,40 +60,11 @@ export function useAutoOpenPermissions(): ToggleHandler {
     }
     getValue();
   }, []);
+
   return {
     value: enabled,
     button: {
-      onClick: toggle,
-      error,
+      onClick: pushAlertOnError(handleAutoOpenPerm),
     },
   };
 }
-
-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
-    // as the result of an input event ...
-    let granted: boolean;
-    try {
-      granted = await platform.getPermissionsApi().requestHostPermissions();
-    } catch (lastError) {
-      onChange(false);
-      throw lastError;
-    }
-    const res = await background.call("toggleHeaderListener", granted);
-    onChange(res.newValue);
-  } else {
-    try {
-      await background
-        .call("toggleHeaderListener", false)
-        .then((r) => onChange(r.newValue));
-    } catch (e) {
-      console.log(e);
-    }
-  }
-  return;
-}
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts 
b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
index 0f035d0f2..25757f473 100644
--- a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts
@@ -14,24 +14,42 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useAlertContext } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { platform } from "../platform/foreground.js";
 
 export function useClipboardPermissions(): ToggleHandler {
   const [enabled, setEnabled] = useState(false);
-  const [error, setError] = useState<TalerError | undefined>();
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
 
-  const toggle = async (): Promise<void> => {
-    return handleClipboardPerm(enabled, setEnabled, api.background).catch(
-      (e) => {
-        setError(TalerError.fromException(e));
-      },
-    );
-  };
+  async function handleClipboardPerm(): Promise<void> {
+    if (!enabled) {
+      // We set permissions here, since apparently FF wants this to be done
+      // as the result of an input event ...
+      let granted: boolean;
+      try {
+        granted = await platform
+          .getPermissionsApi()
+          .requestClipboardPermissions();
+      } catch (lastError) {
+        setEnabled(false);
+        throw lastError;
+      }
+      setEnabled(granted);
+    } else {
+      try {
+        await api.background
+          .call("toggleHeaderListener", false)
+          .then((r) => setEnabled(r.newValue));
+      } catch (e) {
+        console.log(e);
+      }
+    }
+    return;
+  }
 
   useEffect(() => {
     async function getValue(): Promise<void> {
@@ -47,38 +65,7 @@ export function useClipboardPermissions(): ToggleHandler {
   return {
     value: enabled,
     button: {
-      onClick: toggle,
-      error,
+      onClick: pushAlertOnError(handleClipboardPerm),
     },
   };
 }
-
-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
-    // as the result of an input event ...
-    let granted: boolean;
-    try {
-      granted = await platform
-        .getPermissionsApi()
-        .requestClipboardPermissions();
-    } catch (lastError) {
-      onChange(false);
-      throw lastError;
-    }
-    onChange(granted);
-  } else {
-    try {
-      await background
-        .call("toggleHeaderListener", false)
-        .then((r) => onChange(r.newValue));
-    } catch (e) {
-      console.log(e);
-    }
-  }
-  return;
-}
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts 
b/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts
index c04dcce84..6ceae2d47 100644
--- a/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts
@@ -16,6 +16,7 @@
 
 import { ExchangeListItem } from "@gnu-taler/taler-util";
 import { useState } from "preact/hooks";
+import { useAlertContext } from "../context/alert.js";
 import { ButtonHandler } from "../mui/handlers.js";
 
 type State = State.Ready | State.NoExchange | State.Selecting;
@@ -59,6 +60,7 @@ export function useSelectedExchange({
   const [selectedExchange, setSelectedExchange] = useState<string | undefined>(
     undefined,
   );
+  const { pushAlertOnError } = useAlertContext();
 
   if (!list.length) {
     return {
@@ -105,7 +107,7 @@ export function useSelectedExchange({
       return {
         status: "ready",
         doSelect: {
-          onClick: async () => setIsSelecting(true),
+          onClick: pushAlertOnError(async () => setIsSelecting(true)),
         },
         selected: found,
       };
@@ -118,7 +120,7 @@ export function useSelectedExchange({
       return {
         status: "ready",
         doSelect: {
-          onClick: async () => setIsSelecting(true),
+          onClick: pushAlertOnError(async () => setIsSelecting(true)),
         },
         selected: found,
       };
@@ -127,7 +129,7 @@ export function useSelectedExchange({
   return {
     status: "ready",
     doSelect: {
-      onClick: async () => setIsSelecting(true),
+      onClick: pushAlertOnError(async () => setIsSelecting(true)),
     },
     selected: listCurrency[0],
   };
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts 
b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
index 8aabb8adf..e70f7f9be 100644
--- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
@@ -13,11 +13,11 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { useTalerActionURL } from "./useTalerActionURL.js";
-import { mountHook } from "../test-utils.js";
-import { IoCProviderForTesting } from "../context/iocContext.js";
-import { h, VNode } from "preact";
 import { expect } from "chai";
+import { h, VNode } from "preact";
+import { IoCProviderForTesting } from "../context/iocContext.js";
+import { useTalerActionURL } from "./useTalerActionURL.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 
 describe("useTalerActionURL hook", () => {
   it("should be set url to undefined when dismiss", async () => {
@@ -31,32 +31,28 @@ describe("useTalerActionURL hook", () => {
       });
     };
 
-    const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } 
=
-      mountHook(useTalerActionURL, ctx);
-
-    {
-      const [url] = pullLastResultOrThrow();
-      expect(url).undefined;
-    }
-
-    expect(await waitForStateUpdate()).true;
-
-    {
-      const [url, setDismissed] = pullLastResultOrThrow();
-      expect(url).deep.equals({
-        location: "clipboard",
-        uri: "qwe",
-      });
-      setDismissed(true);
-    }
-
-    expect(await waitForStateUpdate()).true;
+    const hookBehavior = await tests.hookBehaveLikeThis(
+      useTalerActionURL,
+      {},
+      [
+        ([url]) => {
+          expect(url).undefined;
+        },
+        ([url, setDismissed]) => {
+          expect(url).deep.equals({
+            location: "clipboard",
+            uri: "qwe",
+          });
+          setDismissed(true);
+        },
+        ([url]) => {
+          if (url !== undefined) throw Error("invalid");
+          expect(url).undefined;
+        },
+      ],
+      ctx,
+    );
 
-    {
-      const [url] = pullLastResultOrThrow();
-      if (url !== undefined) throw Error("invalid");
-      expect(url).undefined;
-    }
-    await assertNoPendingUpdate();
+    expect(hookBehavior).deep.equal({ result: "ok" });
   });
 });
diff --git a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts 
b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
index 6ae55da61..db7effe96 100644
--- a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts
@@ -14,21 +14,28 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { useState, useEffect } from "preact/hooks";
-import { ToggleHandler } from "../mui/handlers.js";
-import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useEffect, useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
 
-export function useWalletDevMode(): ToggleHandler {
+type Result = {
+  value: boolean | undefined;
+  toggle: () => Promise<void>;
+};
+
+export function useWalletDevMode(): Result {
   const [enabled, setEnabled] = useState<undefined | boolean>(undefined);
-  const [error, setError] = useState<TalerError | undefined>();
   const api = useBackendContext();
+  // const { pushAlertOnError } = useAlertContext();
 
-  const toggle = async (): Promise<void> => {
-    return handleOpen(enabled, setEnabled, api).catch((e) => {
-      setError(TalerError.fromException(e));
+  async function handleOpen(): Promise<void> {
+    const nextValue = !enabled;
+    await api.wallet.call(WalletApiOperation.SetDevMode, {
+      devModeEnabled: nextValue,
     });
-  };
+    setEnabled(nextValue);
+    return;
+  }
 
   useEffect(() => {
     async function getValue(): Promise<void> {
@@ -37,24 +44,9 @@ export function useWalletDevMode(): ToggleHandler {
     }
     getValue();
   }, []);
+
   return {
     value: enabled,
-    button: {
-      onClick: enabled === undefined ? undefined : toggle,
-      error,
-    },
+    toggle: handleOpen,
   };
 }
-
-async function handleOpen(
-  currentValue: undefined | boolean,
-  onChange: (value: boolean) => void,
-  api: ReturnType<typeof useBackendContext>,
-): Promise<void> {
-  const nextValue = !currentValue;
-  await api.wallet.call(WalletApiOperation.SetDevMode, {
-    devModeEnabled: nextValue,
-  });
-  onChange(nextValue);
-  return;
-}
diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts 
b/packages/taler-wallet-webextension/src/mui/handlers.ts
index 655fceef9..61786742f 100644
--- a/packages/taler-wallet-webextension/src/mui/handlers.ts
+++ b/packages/taler-wallet-webextension/src/mui/handlers.ts
@@ -14,23 +14,51 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import { AmountJson } from "@gnu-taler/taler-util";
-import { TalerError } from "@gnu-taler/taler-wallet-core";
 
 export interface TextFieldHandler {
-  onInput?: (value: string) => Promise<void>;
+  onInput?: SafeHandler<string>;
   value: string;
   error?: string;
 }
 
 export interface AmountFieldHandler {
-  onInput?: (value: AmountJson) => Promise<void>;
+  onInput?: SafeHandler<AmountJson>;
   value: AmountJson;
   error?: string;
 }
 
+declare const __safe_handler: unique symbol;
+export type SafeHandler<T> = {
+  <Req extends T>(req: Req): Promise<void>;
+  (): Promise<void>;
+  [__safe_handler]: true;
+};
+
+export function withSafe<T>(
+  handler: (p: T) => Promise<void>,
+  onError: (e: Error) => void,
+): SafeHandler<T> {
+  const sh = async function (p: T): Promise<void> {
+    try {
+      await handler(p);
+    } catch (e) {
+      if (e instanceof Error) {
+        onError(e);
+      } else {
+        onError(new Error(String(e)));
+      }
+    }
+  };
+  return sh as SafeHandler<T>;
+}
+
+export const nullFunction = async function (): Promise<void> {
+  //do nothing
+} as SafeHandler<void>;
+
 export interface ButtonHandler {
-  onClick?: () => Promise<void>;
-  error?: TalerError;
+  onClick?: SafeHandler<void>;
+  // error?: TalerError;
 }
 
 export interface ToggleHandler {
@@ -39,7 +67,7 @@ export interface ToggleHandler {
 }
 
 export interface SelectFieldHandler {
-  onChange?: (value: string) => Promise<void>;
+  onChange?: SafeHandler<string>;
   error?: string;
   value: string;
   isDirty?: boolean;
diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx 
b/packages/taler-wallet-webextension/src/popup/Application.tsx
index 13ce71974..c9f98c0fb 100644
--- a/packages/taler-wallet-webextension/src/popup/Application.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Application.tsx
@@ -23,10 +23,10 @@
 import { createHashHistory } from "history";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import Router, { route, Route } from "preact-router";
-import { Match } from "preact-router/match";
 import { useEffect, useState } from "preact/hooks";
 import PendingTransactions from "../components/PendingTransactions.js";
 import { PopupBox } from "../components/styled/index.js";
+import { AlertProvider } from "../context/alert.js";
 import { DevContextProvider } from "../context/devContext.js";
 import { IoCProviderForRuntime } from "../context/iocContext.js";
 import {
@@ -34,7 +34,7 @@ import {
   useTranslationContext,
 } from "../context/translation.js";
 import { useTalerActionURL } from "../hooks/useTalerActionURL.js";
-import { PopupNavBarOptions, Pages, PopupNavBar } from "../NavigationBar.js";
+import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";
 import { platform } from "../platform/foreground.js";
 import { BackupPage } from "../wallet/BackupPage.js";
 import { ProviderDetailPage } from "../wallet/ProviderDetailPage.js";
@@ -219,7 +219,9 @@ function PopupTemplate({
         <PendingTransactions goToTransaction={goToTransaction} />
       ) : undefined}
       <PopupNavBar path={path} />
-      <PopupBox>{children}</PopupBox>
+      <PopupBox>
+        <AlertProvider>{children}</AlertProvider>
+      </PopupBox>
     </Fragment>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
index 8f3762c29..0fe9e7b49 100644
--- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
@@ -19,19 +19,19 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { BalanceView as TestedComponent } from "./BalancePage.js";
 
 export default {
   title: "balance",
 };
 
-export const EmptyBalance = createExample(TestedComponent, {
+export const EmptyBalance = tests.createExample(TestedComponent, {
   balances: [],
   goToWalletManualWithdraw: {},
 });
 
-export const SomeCoins = createExample(TestedComponent, {
+export const SomeCoins = tests.createExample(TestedComponent, {
   balances: [
     {
       available: "USD:10.5",
@@ -45,7 +45,7 @@ export const SomeCoins = createExample(TestedComponent, {
   goToWalletManualWithdraw: {},
 });
 
-export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, {
+export const SomeCoinsInTreeCurrencies = tests.createExample(TestedComponent, {
   balances: [
     {
       available: "EUR:1",
@@ -73,7 +73,7 @@ export const SomeCoinsInTreeCurrencies = 
createExample(TestedComponent, {
   addAction: {},
 });
 
-export const NoCoinsInTreeCurrencies = createExample(TestedComponent, {
+export const NoCoinsInTreeCurrencies = tests.createExample(TestedComponent, {
   balances: [
     {
       available: "EUR:3",
@@ -101,7 +101,7 @@ export const NoCoinsInTreeCurrencies = 
createExample(TestedComponent, {
   addAction: {},
 });
 
-export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, {
+export const SomeCoinsInFiveCurrencies = tests.createExample(TestedComponent, {
   balances: [
     {
       available: "USD:0",
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 96f0f6dd9..87767d008 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -22,7 +22,11 @@ import { BalanceTable } from "../components/BalanceTable.js";
 import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
 import { MultiActionButton } from "../components/MultiActionButton.js";
-import { alertFromError, ErrorAlert } from "../context/alert.js";
+import {
+  alertFromError,
+  ErrorAlert,
+  useAlertContext,
+} from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -75,6 +79,7 @@ function useComponentState({
 }: Props): State {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
+  const { pushAlertOnError } = useAlertContext();
   const [addingAction, setAddingAction] = useState(false);
   const state = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.GetBalances, {}),
@@ -104,7 +109,7 @@ function useComponentState({
       status: "action",
       error: undefined,
       cancel: {
-        onClick: async () => setAddingAction(false),
+        onClick: pushAlertOnError(async () => setAddingAction(false)),
       },
     };
   }
@@ -113,10 +118,10 @@ function useComponentState({
     error: undefined,
     balances: state.response.balances,
     addAction: {
-      onClick: async () => setAddingAction(true),
+      onClick: pushAlertOnError(async () => setAddingAction(true)),
     },
     goToWalletManualWithdraw: {
-      onClick: goToWalletManualWithdraw,
+      onClick: pushAlertOnError(goToWalletManualWithdraw),
     },
     goToWalletDeposit,
     goToWalletHistory,
diff --git 
a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx
index 00293a690..e928cb538 100644
--- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx
@@ -19,33 +19,33 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { TalerActionFound as TestedComponent } from "./TalerActionFound.js";
 
 export default {
   title: "TalerActionFound",
 };
 
-export const PayAction = createExample(TestedComponent, {
+export const PayAction = tests.createExample(TestedComponent, {
   url: "taler://pay/something",
 });
 
-export const WithdrawalAction = createExample(TestedComponent, {
+export const WithdrawalAction = tests.createExample(TestedComponent, {
   url: "taler://withdraw/something",
 });
 
-export const TipAction = createExample(TestedComponent, {
+export const TipAction = tests.createExample(TestedComponent, {
   url: "taler://tip/something",
 });
 
-export const NotifyAction = createExample(TestedComponent, {
+export const NotifyAction = tests.createExample(TestedComponent, {
   url: "taler://notify-reserve/something",
 });
 
-export const RefundAction = createExample(TestedComponent, {
+export const RefundAction = tests.createExample(TestedComponent, {
   url: "taler://refund/something",
 });
 
-export const InvalidAction = createExample(TestedComponent, {
+export const InvalidAction = tests.createExample(TestedComponent, {
   url: "taler://something/asd",
 });
diff --git a/packages/taler-wallet-webextension/src/stories.test.ts 
b/packages/taler-wallet-webextension/src/stories.test.ts
index 47061282d..f02aa5cba 100644
--- a/packages/taler-wallet-webextension/src/stories.test.ts
+++ b/packages/taler-wallet-webextension/src/stories.test.ts
@@ -20,15 +20,17 @@
  */
 import { setupI18n } from "@gnu-taler/taler-util";
 import { parseGroupImport } from "@gnu-taler/web-util/lib/index.browser";
-import { setupPlatform } from "./platform/foreground.js";
 import chromeAPI from "./platform/chrome.js";
-import { renderNodeOrBrowser } from "./test-utils.js";
+import { setupPlatform } from "./platform/foreground.js";
 
 import * as components from "./components/index.stories.js";
 import * as cta from "./cta/index.stories.js";
 import * as mui from "./mui/index.stories.js";
 import * as popup from "./popup/index.stories.js";
 import * as wallet from "./wallet/index.stories.js";
+import { renderNodeOrBrowser } from "./test-utils.js";
+import { h, VNode } from "preact";
+import { AlertProvider } from "./context/alert.js";
 
 setupI18n("en", { en: {} });
 setupPlatform(chromeAPI);
@@ -41,10 +43,15 @@ describe("All the examples:", () => {
         describe(`Component ${component.name}:`, () => {
           component.examples.forEach((example) => {
             it(`should render example: ${example.name}`, () => {
-              renderNodeOrBrowser(
-                example.render.component,
-                example.render.props,
-              );
+              function C(): VNode {
+                const B = h(example.render.component, example.render.props);
+                //FIXME:
+                //some components push the alter in the UI function
+                //that's not correct, should be moved into the sate function
+                // until then, we ran the tests with the alert provider
+                return h(AlertProvider, { children: B }, B);
+              }
+              renderNodeOrBrowser(C, {});
             });
           });
         });
diff --git a/packages/taler-wallet-webextension/src/test-utils.ts 
b/packages/taler-wallet-webextension/src/test-utils.ts
index 7e7ddd88d..085055a7e 100644
--- a/packages/taler-wallet-webextension/src/test-utils.ts
+++ b/packages/taler-wallet-webextension/src/test-utils.ts
@@ -31,8 +31,10 @@ import {
   VNode,
 } from "preact";
 import { render as renderToString } from "preact-render-to-string";
+import { AlertProvider } from "./context/alert.js";
 import { BackendProvider } from "./context/backend.js";
 import { TranslationProvider } from "./context/translation.js";
+import { nullFunction } from "./mui/handlers.js";
 import { BackgroundApiClient, wxApi } from "./wxApi.js";
 
 // When doing tests we want the requestAnimationFrame to be as fast as 
possible.
@@ -218,7 +220,7 @@ export function mountHook<T extends object>(
   };
 }
 
-export const nullFunction: any = () => null;
+// export const nullFunction: any = () => null;
 
 interface MockHandler {
   addWalletCallResponse<Op extends WalletCoreOpKeys>(
@@ -365,6 +367,7 @@ export function createWalletApiMock(): {
     children: ComponentChildren;
   }): VNode {
     let children = _cs;
+    children = create(AlertProvider, { children }, children);
     children = create(TranslationProvider, { children }, children);
     return create(
       BackendProvider,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
index 4ec4c0ffe..e0b79e060 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
@@ -53,7 +53,7 @@ export namespace State {
 
   export interface ConfirmProvider {
     status: "confirm-provider";
-    error: undefined | TalerErrorDetail;
+    error: undefined;
     url: string;
     provider: SyncTermsOfServiceResponse;
     tos: ToggleHandler;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
index 1b30ed0cd..cf35abac7 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts
@@ -14,16 +14,13 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import {
-  canonicalizeBaseUrl,
-  Codec,
-  TalerErrorDetail,
-} from "@gnu-taler/taler-util";
+import { canonicalizeBaseUrl, Codec } from "@gnu-taler/taler-util";
 import {
   codecForSyncTermsOfServiceResponse,
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { assertUnreachable } from "../../utils/index.js";
 import { Props, State } from "./index.js";
@@ -152,17 +149,15 @@ export function useComponentState({
   const [url, setHost] = useState<string | undefined>();
   const [name, setName] = useState<string | undefined>();
   const [tos, setTos] = useState(false);
+  const { pushAlertOnError } = useAlertContext();
   const urlState = useUrlState(
     url,
     "config",
     codecForSyncTermsOfServiceResponse(),
   );
-  const [operationError, setOperationError] = useState<
-    TalerErrorDetail | undefined
-  >();
   const [showConfirm, setShowConfirm] = useState(false);
 
-  async function addBackupProvider() {
+  async function addBackupProvider(): Promise<void> {
     if (!url || !name) return;
 
     const resp = await api.wallet.call(WalletApiOperation.AddBackupProvider, {
@@ -178,8 +173,6 @@ export function useComponentState({
         } else {
           return onComplete(url);
         }
-      case "error":
-        return setOperationError(resp.error);
       case "ok":
         return onComplete(url);
       default:
@@ -190,18 +183,18 @@ export function useComponentState({
   if (showConfirm && urlState && urlState.status === "ok") {
     return {
       status: "confirm-provider",
-      error: operationError,
+      error: undefined,
       onAccept: {
-        onClick: !tos ? undefined : addBackupProvider,
+        onClick: !tos ? undefined : pushAlertOnError(addBackupProvider),
       },
       onCancel: {
-        onClick: onBack,
+        onClick: pushAlertOnError(onBack),
       },
       provider: urlState.result,
       tos: {
         value: tos,
         button: {
-          onClick: async () => setTos(!tos),
+          onClick: pushAlertOnError(async () => setTos(!tos)),
         },
       },
       url: url ?? "",
@@ -213,25 +206,25 @@ export function useComponentState({
     error: undefined,
     name: {
       value: name || "",
-      onInput: async (e) => setName(e),
+      onInput: pushAlertOnError(async (e) => setName(e)),
       error:
         name === undefined ? undefined : !name ? "Can't be empty" : undefined,
     },
     onCancel: {
-      onClick: onBack,
+      onClick: pushAlertOnError(onBack),
     },
     onConfirm: {
       onClick:
         !urlState || urlState.status !== "ok" || !name
           ? undefined
-          : async () => {
+          : pushAlertOnError(async () => {
               setShowConfirm(true);
-            },
+            }),
     },
     urlOk: urlState?.status === "ok",
     url: {
       value: url || "",
-      onInput: async (e) => setHost(e),
+      onInput: pushAlertOnError(async (e) => setHost(e)),
       error: errorString(urlState),
     },
   };
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
index 887ad235e..9d1656ec6 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx
@@ -19,14 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ConfirmProviderView, SelectProviderView } from "./views.js";
 
 export default {
   title: "add backup provider",
 };
 
-export const DemoService = createExample(ConfirmProviderView, {
+export const DemoService = tests.createExample(ConfirmProviderView, {
   url: "https://sync.demo.taler.net/";,
   provider: {
     annual_fee: "KUDOS:0.1",
@@ -40,7 +40,7 @@ export const DemoService = createExample(ConfirmProviderView, 
{
   onCancel: {},
 });
 
-export const FreeService = createExample(ConfirmProviderView, {
+export const FreeService = tests.createExample(ConfirmProviderView, {
   url: "https://sync.taler:9667/";,
   provider: {
     annual_fee: "ARS:0",
@@ -54,14 +54,14 @@ export const FreeService = 
createExample(ConfirmProviderView, {
   onCancel: {},
 });
 
-export const Initial = createExample(SelectProviderView, {
+export const Initial = tests.createExample(SelectProviderView, {
   url: { value: "" },
   name: { value: "" },
   onCancel: {},
   onConfirm: {},
 });
 
-export const WithValue = createExample(SelectProviderView, {
+export const WithValue = tests.createExample(SelectProviderView, {
   url: {
     value: "sync.demo.taler.net",
   },
@@ -72,7 +72,7 @@ export const WithValue = createExample(SelectProviderView, {
   onConfirm: {},
 });
 
-export const WithConnectionError = createExample(SelectProviderView, {
+export const WithConnectionError = tests.createExample(SelectProviderView, {
   url: {
     value: "sync.demo.taler.net",
     error: "Network error",
@@ -84,7 +84,7 @@ export const WithConnectionError = 
createExample(SelectProviderView, {
   onConfirm: {},
 });
 
-export const WithClientError = createExample(SelectProviderView, {
+export const WithClientError = tests.createExample(SelectProviderView, {
   url: {
     value: "sync.demo.taler.net",
     error: "URL may not be right: (404) Not Found",
@@ -96,7 +96,7 @@ export const WithClientError = 
createExample(SelectProviderView, {
   onConfirm: {},
 });
 
-export const WithServerError = createExample(SelectProviderView, {
+export const WithServerError = tests.createExample(SelectProviderView, {
   url: {
     value: "sync.demo.taler.net",
     error: "Try another server: (500) Internal Server Error",
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
index 3241a3ab0..a939c9268 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts
@@ -21,7 +21,8 @@
 
 import { expect } from "chai";
 import { tests } from "../../../../web-util/src/index.browser.js";
-import { createWalletApiMock, nullFunction } from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
 import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
index f5db3825d..8c45ae050 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { AddNewActionView as TestedComponent } from "./AddNewActionView.js";
 
 export default {
@@ -30,4 +30,4 @@ export default {
   },
 };
 
-export const Initial = createExample(TestedComponent, {});
+export const Initial = tests.createExample(TestedComponent, {});
diff --git a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
index c3a1ea5d6..8ae1a76ce 100644
--- a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
@@ -25,14 +25,14 @@ import {
   BackupView as TestedComponent,
   ShowRecoveryInfo,
 } from "./BackupPage.js";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 
 export default {
   title: "backup",
 };
 
-export const LotOfProviders = createExample(TestedComponent, {
+export const LotOfProviders = tests.createExample(TestedComponent, {
   providers: [
     {
       active: true,
@@ -164,7 +164,7 @@ export const LotOfProviders = 
createExample(TestedComponent, {
   ],
 });
 
-export const OneProvider = createExample(TestedComponent, {
+export const OneProvider = tests.createExample(TestedComponent, {
   providers: [
     {
       active: true,
@@ -190,10 +190,10 @@ export const OneProvider = createExample(TestedComponent, 
{
   ],
 });
 
-export const Empty = createExample(TestedComponent, {
+export const Empty = tests.createExample(TestedComponent, {
   providers: [],
 });
 
-export const Recovery = createExample(ShowRecoveryInfo, {
+export const Recovery = tests.createExample(ShowRecoveryInfo, {
   info: "taler://recovery/ASLDKJASLKDJASD",
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index 48c9c9cb1..c3abb570b 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -29,7 +29,7 @@ import {
 } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
-import { AlertView } from "../components/CurrentAlerts.js";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
 import { QR } from "../components/QR.js";
 import {
@@ -118,8 +118,8 @@ export function BackupPage({ onAddProvider }: Props): VNode 
{
   }
   if (status.hasError) {
     return (
-      <AlertView
-        alert={alertFromError(
+      <ErrorAlertView
+        error={alertFromError(
           i18n.str`Could not load backup providers`,
           status,
         )}
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index b597c77be..935adf012 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -25,7 +25,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -39,6 +39,7 @@ export function useComponentState({
 }: Props): State {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
+  const { pushAlertOnError } = useAlertContext();
   const parsed = amountStr === undefined ? undefined : 
Amounts.parse(amountStr);
   const currency = parsed !== undefined ? parsed.currency : currencyStr;
 
@@ -130,9 +131,9 @@ export function useComponentState({
       error: undefined,
       currency,
       onAddAccount: {
-        onClick: async () => {
+        onClick: pushAlertOnError(async () => {
           setAddingAccount(true);
-        },
+        }),
       },
     };
   }
@@ -221,27 +222,27 @@ export function useComponentState({
     currency,
     amount: {
       value: amount,
-      onInput: updateAmount,
+      onInput: pushAlertOnError(updateAmount),
       error: amountError,
     },
     onAddAccount: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         setAddingAccount(true);
-      },
+      }),
     },
     account: {
       list: accountMap,
       value: stringifyPaytoUri(currentAccount),
-      onChange: updateAccountFromList,
+      onChange: pushAlertOnError(updateAccountFromList),
     },
     currentAccount,
     cancelHandler: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         onCancel(currency);
-      },
+      }),
     },
     depositHandler: {
-      onClick: unableToDeposit ? undefined : doSend,
+      onClick: unableToDeposit ? undefined : pushAlertOnError(doSend),
     },
     totalFee,
     totalToDeposit,
@@ -263,7 +264,7 @@ async function getFeeForAmount(
   });
 }
 
-export function labelForAccountType(id: string) {
+export function labelForAccountType(id: string): string {
   switch (id) {
     case "":
       return "Choose one";
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
index b4d1060eb..99f08477f 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
@@ -19,26 +19,21 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { Amounts, DepositGroupFees } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
-import { labelForAccountType } from "./state.js";
+import { Amounts } from "@gnu-taler/taler-util";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { nullFunction } from "../../mui/handlers.js";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "deposit",
 };
 
-// const ac = parsePaytoUri("payto://iban/ES8877998399652238")!;
-// const accountMap = createLabelsForBankAccount([ac]);
-
-export const WithNoAccountForIBAN = createExample(ReadyView, {
+export const WithNoAccountForIBAN = tests.createExample(ReadyView, {
   status: "ready",
   account: {
     list: {},
     value: "",
-    onChange: async () => {
-      null;
-    },
+    onChange: nullFunction,
   },
   currentAccount: {
     isKnown: true,
@@ -49,31 +44,25 @@ export const WithNoAccountForIBAN = 
createExample(ReadyView, {
   },
   currency: "USD",
   amount: {
-    onInput: async () => {
-      null;
-    },
+    onInput: nullFunction,
     value: Amounts.parseOrThrow("USD:10"),
   },
   onAddAccount: {},
   cancelHandler: {},
   depositHandler: {
-    onClick: async () => {
-      return;
-    },
+    onClick: nullFunction,
   },
   totalFee: Amounts.zeroOfCurrency("USD"),
   totalToDeposit: Amounts.parseOrThrow("USD:10"),
   // onCalculateFee: alwaysReturnFeeToOne,
 });
 
-export const WithIBANAccountTypeSelected = createExample(ReadyView, {
+export const WithIBANAccountTypeSelected = tests.createExample(ReadyView, {
   status: "ready",
   account: {
     list: { asdlkajsdlk: "asdlkajsdlk", qwerqwer: "qwerqwer" },
     value: "asdlkajsdlk",
-    onChange: async () => {
-      null;
-    },
+    onChange: nullFunction,
   },
   currentAccount: {
     isKnown: true,
@@ -84,31 +73,25 @@ export const WithIBANAccountTypeSelected = 
createExample(ReadyView, {
   },
   currency: "USD",
   amount: {
-    onInput: async () => {
-      null;
-    },
+    onInput: nullFunction,
     value: Amounts.parseOrThrow("USD:10"),
   },
   onAddAccount: {},
   cancelHandler: {},
   depositHandler: {
-    onClick: async () => {
-      return;
-    },
+    onClick: nullFunction,
   },
   totalFee: Amounts.zeroOfCurrency("USD"),
   totalToDeposit: Amounts.parseOrThrow("USD:10"),
   // onCalculateFee: alwaysReturnFeeToOne,
 });
 
-export const NewBitcoinAccountTypeSelected = createExample(ReadyView, {
+export const NewBitcoinAccountTypeSelected = tests.createExample(ReadyView, {
   status: "ready",
   account: {
     list: {},
     value: "asdlkajsdlk",
-    onChange: async () => {
-      null;
-    },
+    onChange: nullFunction,
   },
   currentAccount: {
     isKnown: true,
@@ -120,16 +103,12 @@ export const NewBitcoinAccountTypeSelected = 
createExample(ReadyView, {
   onAddAccount: {},
   currency: "USD",
   amount: {
-    onInput: async () => {
-      null;
-    },
+    onInput: nullFunction,
     value: Amounts.parseOrThrow("USD:10"),
   },
   cancelHandler: {},
   depositHandler: {
-    onClick: async () => {
-      return;
-    },
+    onClick: nullFunction,
   },
   totalFee: Amounts.zeroOfCurrency("USD"),
   totalToDeposit: Amounts.parseOrThrow("USD:10"),
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index b222709a7..0054ab5af 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -28,7 +28,8 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
 import { tests } from "../../../../web-util/src/index.browser.js";
-import { createWalletApiMock, nullFunction } from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
 
 import { useComponentState } from "./state.js";
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
index 1fe324c5a..d5015ae1d 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -17,7 +17,7 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -26,6 +26,7 @@ import { Contact, Props, State } from "./index.js";
 
 export function useComponentState(props: Props): RecursiveState<State> {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const parsedInitialAmount = !props.amount
     ? undefined
     : Amounts.parse(props.amount);
@@ -108,26 +109,26 @@ export function useComponentState(props: Props): 
RecursiveState<State> {
         error: undefined,
         previous,
         selectCurrency: {
-          onClick: async () => {
+          onClick: pushAlertOnError(async () => {
             setAmount(undefined);
-          },
+          }),
         },
         goToBank: {
           onClick: invalid
             ? undefined
-            : async () => {
+            : pushAlertOnError(async () => {
                 props.goToWalletBankDeposit(currencyAndAmount);
-              },
+              }),
         },
         goToWallet: {
           onClick: invalid
             ? undefined
-            : async () => {
+            : pushAlertOnError(async () => {
                 props.goToWalletWalletSend(currencyAndAmount);
-              },
+              }),
         },
         amountHandler: {
-          onInput: async (s) => setAmount(s),
+          onInput: pushAlertOnError(async (s) => setAmount(s)),
           value: amount,
         },
         type: props.type,
@@ -138,26 +139,26 @@ export function useComponentState(props: Props): 
RecursiveState<State> {
         error: undefined,
         previous,
         selectCurrency: {
-          onClick: async () => {
+          onClick: pushAlertOnError(async () => {
             setAmount(undefined);
-          },
+          }),
         },
         goToBank: {
           onClick: invalid
             ? undefined
-            : async () => {
+            : pushAlertOnError(async () => {
                 props.goToWalletManualWithdraw(currencyAndAmount);
-              },
+              }),
         },
         goToWallet: {
           onClick: invalid
             ? undefined
-            : async () => {
+            : pushAlertOnError(async () => {
                 props.goToWalletWalletInvoice(currencyAndAmount);
-              },
+              }),
         },
         amountHandler: {
-          onInput: async (s) => setAmount(s),
+          onInput: pushAlertOnError(async (s) => setAmount(s)),
           value: amount,
         },
         type: props.type,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
index ffec8ba36..111f47776 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx
@@ -19,14 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView, SelectCurrencyView } from "./views.js";
 
 export default {
   title: "destination",
 };
 
-export const GetCash = createExample(ReadyView, {
+export const GetCash = tests.createExample(ReadyView, {
   amountHandler: {
     value: {
       currency: "EUR",
@@ -40,7 +40,7 @@ export const GetCash = createExample(ReadyView, {
   selectCurrency: {},
   type: "get",
 });
-export const SendCash = createExample(ReadyView, {
+export const SendCash = tests.createExample(ReadyView, {
   amountHandler: {
     value: {
       currency: "EUR",
@@ -55,7 +55,7 @@ export const SendCash = createExample(ReadyView, {
   type: "send",
 });
 
-export const SelectCurrency = createExample(SelectCurrencyView, {
+export const SelectCurrency = tests.createExample(SelectCurrencyView, {
   currencies: {
     "": "Select a currency",
     USD: "USD",
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
index cc511ce65..b079ef0e8 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts
@@ -28,7 +28,8 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
 import { tests } from "../../../../web-util/src/index.browser.js";
-import { createWalletApiMock, nullFunction } from "../../test-utils.js";
+import { nullFunction } from "../../mui/handlers.js";
+import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 const exchangeArs: ExchangeListItem = {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx
index d9a5a8fd7..d4ccb60e6 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx
@@ -20,7 +20,7 @@
  */
 
 import { PendingTaskType } from "@gnu-taler/taler-wallet-core";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { View as TestedComponent } from "./DeveloperPage.js";
 
 export default {
@@ -31,7 +31,7 @@ export default {
   },
 };
 
-export const AllOff = createExample(TestedComponent, {
+export const AllOff = tests.createExample(TestedComponent, {
   onDownloadDatabase: async () => "this is the content of the database",
   operations: [
     {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx
index 696e424c4..e157e6e6f 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "example",
 };
 
-export const Ready = createExample(ReadyView, {});
+export const Ready = tests.createExample(ReadyView, {});
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx
index 8fbecfc4c..216e50385 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ExchangeAddConfirmPage as TestedComponent } from 
"./ExchangeAddConfirm.js";
 
 export default {
@@ -32,14 +32,14 @@ export default {
   },
 };
 
-export const TermsNotFound = createExample(TestedComponent, {
+export const TermsNotFound = tests.createExample(TestedComponent, {
   url: "https://exchange.demo.taler.net/";,
 });
 
-export const NewTerms = createExample(TestedComponent, {
+export const NewTerms = tests.createExample(TestedComponent, {
   url: "https://exchange.demo.taler.net/";,
 });
 
-export const TermsChanged = createExample(TestedComponent, {
+export const TermsChanged = tests.createExample(TestedComponent, {
   url: "https://exchange.demo.taler.net/";,
 });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx
index cd86ad8c6..914ca8ae4 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { queryToSlashKeys } from "../utils/index.js";
 import { ExchangeSetUrlPage as TestedComponent } from "./ExchangeSetUrl.js";
 
@@ -27,17 +27,17 @@ export default {
   title: "exchange add set url",
 };
 
-export const ExpectedUSD = createExample(TestedComponent, {
+export const ExpectedUSD = tests.createExample(TestedComponent, {
   expectedCurrency: "USD",
   onVerify: queryToSlashKeys,
 });
 
-export const ExpectedKUDOS = createExample(TestedComponent, {
+export const ExpectedKUDOS = tests.createExample(TestedComponent, {
   expectedCurrency: "KUDOS",
   onVerify: queryToSlashKeys,
 });
 
-export const InitialState = createExample(TestedComponent, {
+export const InitialState = tests.createExample(TestedComponent, {
   onVerify: queryToSlashKeys,
 });
 
@@ -55,7 +55,7 @@ const knownExchanges = [
   },
 ];
 
-export const WithDemoAsKnownExchange = createExample(TestedComponent, {
+export const WithDemoAsKnownExchange = tests.createExample(TestedComponent, {
   onVerify: async (url) => {
     const found =
       knownExchanges.findIndex((e) => e.exchangeBaseUrl === url) !== -1;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
index cfb32cbbb..7ad11e67c 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
@@ -20,7 +20,7 @@ import {
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -33,6 +33,7 @@ export function useComponentState({
   currentExchange,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const { i18n } = useTranslationContext();
   const initialValue = exchanges.findIndex(
     (e) => e.exchangeBaseUrl === currentExchange,
@@ -115,7 +116,7 @@ export function useComponentState({
       status: "showing-privacy",
       error: undefined,
       onClose: {
-        onClick: async () => setShowingPrivacy(undefined),
+        onClick: pushAlertOnError(async () => setShowingPrivacy(undefined)),
       },
       exchangeUrl: showingPrivacy,
     };
@@ -125,7 +126,7 @@ export function useComponentState({
       status: "showing-tos",
       error: undefined,
       onClose: {
-        onClick: async () => setShowingTos(undefined),
+        onClick: pushAlertOnError(async () => setShowingTos(undefined)),
       },
       exchangeUrl: showingTos,
     };
@@ -138,24 +139,24 @@ export function useComponentState({
       exchanges: {
         list: exchangeMap,
         value: value,
-        onChange: async (v) => {
+        onChange: pushAlertOnError(async (v) => {
           setValue(v);
-        },
+        }),
       },
       error: undefined,
       onClose: {
-        onClick: onCancel,
+        onClick: pushAlertOnError(onCancel),
       },
       selected,
       onShowPrivacy: {
-        onClick: async () => {
+        onClick: pushAlertOnError(async () => {
           setShowingPrivacy(selected.exchangeBaseUrl);
-        },
+        }),
       },
       onShowTerms: {
-        onClick: async () => {
+        onClick: pushAlertOnError(async () => {
           setShowingTos(selected.exchangeBaseUrl);
-        },
+        }),
       },
     };
   }
@@ -215,30 +216,30 @@ export function useComponentState({
     exchanges: {
       list: exchangeMap,
       value: value,
-      onChange: async (v) => {
+      onChange: pushAlertOnError(async (v) => {
         setValue(v);
-      },
+      }),
     },
     error: undefined,
     onReset: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         setValue(String(initialValue));
-      },
+      }),
     },
     onSelect: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         onSelection(selected.exchangeBaseUrl);
-      },
+      }),
     },
     onShowPrivacy: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         setShowingPrivacy(selected.exchangeBaseUrl);
-      },
+      }),
     },
     onShowTerms: {
-      onClick: async () => {
+      onClick: pushAlertOnError(async () => {
         setShowingTos(selected.exchangeBaseUrl);
-      },
+      }),
     },
     selected,
     coinOperationTimeline,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx
index 05765b50a..a65f85c6a 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx
@@ -19,14 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ComparingView, ReadyView } from "./views.js";
 
 export default {
   title: "select exchange",
 };
 
-export const Bitcoin1 = createExample(ReadyView, {
+export const Bitcoin1 = tests.createExample(ReadyView, {
   exchanges: {
     list: { "0": "https://exchange.taler.ar"; },
     value: "0",
@@ -43,7 +43,7 @@ export const Bitcoin1 = createExample(ReadyView, {
   onShowTerms: {},
   onClose: {},
 });
-export const Bitcoin2 = createExample(ReadyView, {
+export const Bitcoin2 = tests.createExample(ReadyView, {
   exchanges: {
     list: {
       "https://exchange.taler.ar": "https://exchange.taler.ar";,
@@ -64,7 +64,7 @@ export const Bitcoin2 = createExample(ReadyView, {
   onClose: {},
 });
 
-export const Kudos1 = createExample(ReadyView, {
+export const Kudos1 = tests.createExample(ReadyView, {
   exchanges: {
     list: {
       "https://exchange-kudos.taler.ar": "https://exchange-kudos.taler.ar";,
@@ -83,7 +83,7 @@ export const Kudos1 = createExample(ReadyView, {
   onShowTerms: {},
   onClose: {},
 });
-export const Kudos2 = createExample(ReadyView, {
+export const Kudos2 = tests.createExample(ReadyView, {
   exchanges: {
     list: {
       "https://exchange-kudos.taler.ar": "https://exchange-kudos.taler.ar";,
@@ -103,7 +103,7 @@ export const Kudos2 = createExample(ReadyView, {
   onShowTerms: {},
   onClose: {},
 });
-export const ComparingBitcoin = createExample(ComparingView, {
+export const ComparingBitcoin = tests.createExample(ComparingView, {
   exchanges: {
     list: { "http://exchange": "http://exchange"; },
     value: "http://exchange";,
@@ -131,7 +131,7 @@ export const ComparingBitcoin = 
createExample(ComparingView, {
   missingWireTYpe: [],
   wireFeeTimeline: {},
 });
-export const ComparingKudos = createExample(ComparingView, {
+export const ComparingKudos = tests.createExample(ComparingView, {
   exchanges: {
     list: { "http://exchange": "http://exchange"; },
     value: "http://exchange";,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index 1674ac135..13f4c8230 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -37,7 +37,7 @@ import {
   WithdrawalType,
 } from "@gnu-taler/taler-util";
 import { HistoryView as TestedComponent } from "./History.js";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 
 export default {
   title: "balance",
@@ -160,25 +160,28 @@ const exampleData = {
   } as TransactionPeerPullDebit,
 };
 
-export const NoBalance = createExample(TestedComponent, {
+export const NoBalance = tests.createExample(TestedComponent, {
   transactions: [],
   balances: [],
 });
 
-export const SomeBalanceWithNoTransactions = createExample(TestedComponent, {
-  transactions: [],
-  balances: [
-    {
-      available: "TESTKUDOS:10",
-      pendingIncoming: "TESTKUDOS:0",
-      pendingOutgoing: "TESTKUDOS:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
+export const SomeBalanceWithNoTransactions = tests.createExample(
+  TestedComponent,
+  {
+    transactions: [],
+    balances: [
+      {
+        available: "TESTKUDOS:10",
+        pendingIncoming: "TESTKUDOS:0",
+        pendingOutgoing: "TESTKUDOS:0",
+        hasPendingTransactions: false,
+        requiresUserInput: false,
+      },
+    ],
+  },
+);
 
-export const OneSimpleTransaction = createExample(TestedComponent, {
+export const OneSimpleTransaction = tests.createExample(TestedComponent, {
   transactions: [exampleData.withdraw],
   balances: [
     {
@@ -191,20 +194,23 @@ export const OneSimpleTransaction = 
createExample(TestedComponent, {
   ],
 });
 
-export const TwoTransactionsAndZeroBalance = createExample(TestedComponent, {
-  transactions: [exampleData.withdraw, exampleData.deposit],
-  balances: [
-    {
-      available: "USD:0",
-      pendingIncoming: "USD:0",
-      pendingOutgoing: "USD:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
+export const TwoTransactionsAndZeroBalance = tests.createExample(
+  TestedComponent,
+  {
+    transactions: [exampleData.withdraw, exampleData.deposit],
+    balances: [
+      {
+        available: "USD:0",
+        pendingIncoming: "USD:0",
+        pendingOutgoing: "USD:0",
+        hasPendingTransactions: false,
+        requiresUserInput: false,
+      },
+    ],
+  },
+);
 
-export const OneTransactionPending = createExample(TestedComponent, {
+export const OneTransactionPending = tests.createExample(TestedComponent, {
   transactions: [
     {
       ...exampleData.withdraw,
@@ -222,7 +228,7 @@ export const OneTransactionPending = 
createExample(TestedComponent, {
   ],
 });
 
-export const SomeTransactions = createExample(TestedComponent, {
+export const SomeTransactions = tests.createExample(TestedComponent, {
   transactions: [
     exampleData.withdraw,
     exampleData.payment,
@@ -251,7 +257,7 @@ export const SomeTransactions = 
createExample(TestedComponent, {
   ],
 });
 
-export const SomeTransactionsWithTwoCurrencies = createExample(
+export const SomeTransactionsWithTwoCurrencies = tests.createExample(
   TestedComponent,
   {
     transactions: [
@@ -283,7 +289,7 @@ export const SomeTransactionsWithTwoCurrencies = 
createExample(
   },
 );
 
-export const FiveOfficialCurrencies = createExample(TestedComponent, {
+export const FiveOfficialCurrencies = tests.createExample(TestedComponent, {
   transactions: [exampleData.withdraw],
   balances: [
     {
@@ -324,7 +330,7 @@ export const FiveOfficialCurrencies = 
createExample(TestedComponent, {
   ],
 });
 
-export const FiveOfficialCurrenciesWithHighValue = createExample(
+export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
   TestedComponent,
   {
     transactions: [exampleData.withdraw],
@@ -368,7 +374,7 @@ export const FiveOfficialCurrenciesWithHighValue = 
createExample(
   },
 );
 
-export const PeerToPeer = createExample(TestedComponent, {
+export const PeerToPeer = tests.createExample(TestedComponent, {
   transactions: [
     exampleData.pull_credit,
     exampleData.pull_debit,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 143d3adbb..1d51f835a 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -23,7 +23,7 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
-import { AlertView } from "../components/CurrentAlerts.js";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
 import {
   CenteredBoldText,
@@ -33,7 +33,7 @@ import {
 } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
 import { TransactionItem } from "../components/TransactionItem.js";
-import { alertFromError } from "../context/alert.js";
+import { alertFromError, useAlertContext } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -72,8 +72,8 @@ export function HistoryPage({
 
   if (state.hasError) {
     return (
-      <AlertView
-        alert={alertFromError(
+      <ErrorAlertView
+        error={alertFromError(
           i18n.str`Could not load the list of transactions`,
           state,
         )}
@@ -112,6 +112,7 @@ export function HistoryView({
 }): VNode {
   const { i18n } = useTranslationContext();
   const currencies = balances.map((b) => b.available.split(":")[0]);
+  const { pushAlertOnError } = useAlertContext();
 
   const defaultCurrencyIndex = currencies.findIndex(
     (c) => c === defaultCurrency,
@@ -145,7 +146,7 @@ export function HistoryView({
     return (
       <NoBalanceHelp
         goToWalletManualWithdraw={{
-          onClick: goToWalletManualWithdraw,
+          onClick: pushAlertOnError(goToWalletManualWithdraw),
         }}
       />
     );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
index 176a8d100..f7383d483 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
@@ -21,7 +21,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
-import { alertFromError } from "../../context/alert.js";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -33,6 +33,7 @@ export function useComponentState({
   onCancel,
 }: Props): State {
   const api = useBackendContext();
+  const { pushAlertOnError } = useAlertContext();
   const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
@@ -109,30 +110,30 @@ export function useComponentState({
     accountType: {
       list: accountType,
       value: type,
-      onChange: async (v) => {
+      onChange: pushAlertOnError(async (v) => {
         setType(v);
-      },
+      }),
     },
     alias: {
       value: alias,
-      onInput: async (v) => {
+      onInput: pushAlertOnError(async (v) => {
         setAlias(v);
-      },
+      }),
     },
     uri: {
       value: payto,
       error: paytoUriError,
-      onInput: async (v) => {
+      onInput: pushAlertOnError(async (v) => {
         setPayto(v);
-      },
+      }),
     },
     accountByType,
     deleteAccount,
     onAccountAdded: {
-      onClick: unableToAdd ? undefined : addAccount,
+      onClick: unableToAdd ? undefined : pushAlertOnError(addAccount),
     },
     onCancel: {
-      onClick: async () => onCancel(),
+      onClick: pushAlertOnError(async () => onCancel()),
     },
   };
 }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx
index ca6db8be9..e20d4e0e8 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx
@@ -19,18 +19,15 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { nullFunction } from "../../mui/handlers.js";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "manage account",
 };
 
-const nullFunction = async () => {
-  null;
-};
-
-export const JustTwoBitcoinAccounts = createExample(ReadyView, {
+export const JustTwoBitcoinAccounts = tests.createExample(ReadyView, {
   status: "ready",
   currency: "ARS",
   accountType: {
@@ -84,7 +81,7 @@ export const JustTwoBitcoinAccounts = 
createExample(ReadyView, {
   onCancel: {},
 });
 
-export const WithAllTypeOfAccounts = createExample(ReadyView, {
+export const WithAllTypeOfAccounts = tests.createExample(ReadyView, {
   status: "ready",
   currency: "ARS",
   accountType: {
@@ -165,7 +162,7 @@ export const WithAllTypeOfAccounts = 
createExample(ReadyView, {
   onCancel: {},
 });
 
-export const AddingIbanAccount = createExample(ReadyView, {
+export const AddingIbanAccount = tests.createExample(ReadyView, {
   status: "ready",
   currency: "ARS",
   accountType: {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx
index c4da99909..bce4a8f41 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx
@@ -20,14 +20,14 @@
  */
 
 import { AbsoluteTime, AttentionType } from "@gnu-taler/taler-util";
-import { createExample } from "../../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReadyView } from "./views.js";
 
 export default {
   title: "notifications",
 };
 
-export const Ready = createExample(ReadyView, {
+export const Ready = tests.createExample(ReadyView, {
   list: [
     {
       when: AbsoluteTime.now(),
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx
index 9ca397302..55f80a397 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ConfirmProviderView as TestedComponent } from "./ProviderAddPage.js";
 
 export default {
@@ -32,7 +32,7 @@ export default {
   },
 };
 
-export const DemoService = createExample(TestedComponent, {
+export const DemoService = tests.createExample(TestedComponent, {
   url: "https://sync.demo.taler.net/";,
   provider: {
     annual_fee: "KUDOS:0.1",
@@ -41,7 +41,7 @@ export const DemoService = createExample(TestedComponent, {
   },
 });
 
-export const FreeService = createExample(TestedComponent, {
+export const FreeService = tests.createExample(TestedComponent, {
   url: "https://sync.taler:9667/";,
   provider: {
     annual_fee: "ARS:0",
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx
index a5528c36b..b4f2533bd 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { SetUrlView as TestedComponent } from "./ProviderAddPage.js";
 
 export default {
@@ -32,20 +32,20 @@ export default {
   },
 };
 
-export const Initial = createExample(TestedComponent, {});
+export const Initial = tests.createExample(TestedComponent, {});
 
-export const WithValue = createExample(TestedComponent, {
+export const WithValue = tests.createExample(TestedComponent, {
   initialValue: "sync.demo.taler.net",
 });
 
-export const WithConnectionError = createExample(TestedComponent, {
+export const WithConnectionError = tests.createExample(TestedComponent, {
   withError: "Network error",
 });
 
-export const WithClientError = createExample(TestedComponent, {
+export const WithClientError = tests.createExample(TestedComponent, {
   withError: "URL may not be right: (404) Not Found",
 });
 
-export const WithServerError = createExample(TestedComponent, {
+export const WithServerError = tests.createExample(TestedComponent, {
   withError: "Try another server: (500) Internal Server Error",
 });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
index 98c68e6bd..08f26438f 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
@@ -21,7 +21,7 @@
 
 import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ProviderView as TestedComponent } from "./ProviderDetailPage.js";
 
 export default {
@@ -34,7 +34,7 @@ export default {
   },
 };
 
-export const Active = createExample(TestedComponent, {
+export const Active = tests.createExample(TestedComponent, {
   info: {
     active: true,
     name: "sync.demo",
@@ -58,7 +58,7 @@ export const Active = createExample(TestedComponent, {
   },
 });
 
-export const ActiveErrorSync = createExample(TestedComponent, {
+export const ActiveErrorSync = tests.createExample(TestedComponent, {
   info: {
     active: true,
     name: "sync.demo",
@@ -79,6 +79,7 @@ export const ActiveErrorSync = createExample(TestedComponent, 
{
     lastError: {
       code: 2002,
       details: "details",
+      when: new Date().toISOString(),
       hint: "error hint from the server",
       message: "message",
     },
@@ -90,34 +91,37 @@ export const ActiveErrorSync = 
createExample(TestedComponent, {
   },
 });
 
-export const ActiveBackupProblemUnreadable = createExample(TestedComponent, {
-  info: {
-    active: true,
-    name: "sync.demo",
-    syncProviderBaseUrl: "http://sync.taler:9967/";,
-    lastSuccessfulBackupTimestamp:
-      TalerProtocolTimestamp.fromSeconds(1625063925),
-    paymentProposalIds: [
-      "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
-    ],
-    paymentStatus: {
-      type: ProviderPaymentType.Paid,
-      paidUntil: {
-        t_ms: 1656599921000,
+export const ActiveBackupProblemUnreadable = tests.createExample(
+  TestedComponent,
+  {
+    info: {
+      active: true,
+      name: "sync.demo",
+      syncProviderBaseUrl: "http://sync.taler:9967/";,
+      lastSuccessfulBackupTimestamp:
+        TalerProtocolTimestamp.fromSeconds(1625063925),
+      paymentProposalIds: [
+        "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
+      ],
+      paymentStatus: {
+        type: ProviderPaymentType.Paid,
+        paidUntil: {
+          t_ms: 1656599921000,
+        },
+      },
+      backupProblem: {
+        type: "backup-unreadable",
+      },
+      terms: {
+        annualFee: "EUR:1",
+        storageLimitInMegabytes: 16,
+        supportedProtocolVersion: "0.0",
       },
-    },
-    backupProblem: {
-      type: "backup-unreadable",
-    },
-    terms: {
-      annualFee: "EUR:1",
-      storageLimitInMegabytes: 16,
-      supportedProtocolVersion: "0.0",
     },
   },
-});
+);
 
-export const ActiveBackupProblemDevice = createExample(TestedComponent, {
+export const ActiveBackupProblemDevice = tests.createExample(TestedComponent, {
   info: {
     active: true,
     name: "sync.demo",
@@ -149,7 +153,7 @@ export const ActiveBackupProblemDevice = 
createExample(TestedComponent, {
   },
 });
 
-export const InactiveUnpaid = createExample(TestedComponent, {
+export const InactiveUnpaid = tests.createExample(TestedComponent, {
   info: {
     active: false,
     name: "sync.demo",
@@ -166,25 +170,28 @@ export const InactiveUnpaid = 
createExample(TestedComponent, {
   },
 });
 
-export const InactiveInsufficientBalance = createExample(TestedComponent, {
-  info: {
-    active: false,
-    name: "sync.demo",
-    syncProviderBaseUrl: "http://sync.demo.taler.net/";,
-    paymentProposalIds: [],
-    paymentStatus: {
-      type: ProviderPaymentType.InsufficientBalance,
-      amount: "EUR:123",
-    },
-    terms: {
-      annualFee: "EUR:0.1",
-      storageLimitInMegabytes: 16,
-      supportedProtocolVersion: "0.0",
+export const InactiveInsufficientBalance = tests.createExample(
+  TestedComponent,
+  {
+    info: {
+      active: false,
+      name: "sync.demo",
+      syncProviderBaseUrl: "http://sync.demo.taler.net/";,
+      paymentProposalIds: [],
+      paymentStatus: {
+        type: ProviderPaymentType.InsufficientBalance,
+        amount: "EUR:123",
+      },
+      terms: {
+        annualFee: "EUR:0.1",
+        storageLimitInMegabytes: 16,
+        supportedProtocolVersion: "0.0",
+      },
     },
   },
-});
+);
 
-export const InactivePending = createExample(TestedComponent, {
+export const InactivePending = tests.createExample(TestedComponent, {
   info: {
     active: false,
     name: "sync.demo",
@@ -202,7 +209,7 @@ export const InactivePending = 
createExample(TestedComponent, {
   },
 });
 
-export const ActiveTermsChanged = createExample(TestedComponent, {
+export const ActiveTermsChanged = tests.createExample(TestedComponent, {
   info: {
     active: true,
     name: "sync.demo",
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index 9b72c0fae..789465a87 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -23,7 +23,7 @@ import {
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
-import { AlertView } from "../components/CurrentAlerts.js";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { ErrorMessage } from "../components/ErrorMessage.js";
 import { Loading } from "../components/Loading.js";
 import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
@@ -66,8 +66,8 @@ export function ProviderDetailPage({
   }
   if (state.hasError) {
     return (
-      <AlertView
-        alert={alertFromError(
+      <ErrorAlertView
+        error={alertFromError(
           i18n.str`There was an error loading the provider detail for 
&quot;${providerURL}&quot;`,
           state,
         )}
diff --git a/packages/taler-wallet-webextension/src/wallet/QrReader.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/QrReader.stories.tsx
index 0fc38e90f..074fdc4fa 100644
--- a/packages/taler-wallet-webextension/src/wallet/QrReader.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/QrReader.stories.tsx
@@ -19,11 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { QrReaderPage } from "./QrReader.js";
 
 export default {
   title: "qr reader",
 };
 
-export const Reading = createExample(QrReaderPage, {});
+export const Reading = tests.createExample(QrReaderPage, {});
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
index 7ea3b386b..1d2893b43 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
@@ -20,7 +20,7 @@
  */
 
 import { parsePaytoUri } from "@gnu-taler/taler-util";
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { ReserveCreated as TestedComponent } from "./ReserveCreated.js";
 
 export default {
@@ -29,7 +29,7 @@ export default {
   argTypes: {},
 };
 
-export const TalerBank = createExample(TestedComponent, {
+export const TalerBank = tests.createExample(TestedComponent, {
   reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
   paytoURI: parsePaytoUri(
     
"payto://x-taler-bank/bank.taler:5882/exchangeminator?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
@@ -42,7 +42,7 @@ export const TalerBank = createExample(TestedComponent, {
   exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
 
-export const IBAN = createExample(TestedComponent, {
+export const IBAN = tests.createExample(TestedComponent, {
   reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
   paytoURI: parsePaytoUri(
     
"payto://iban/ES8877998399652238?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
@@ -55,7 +55,7 @@ export const IBAN = createExample(TestedComponent, {
   exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
 
-export const WithReceiverName = createExample(TestedComponent, {
+export const WithReceiverName = tests.createExample(TestedComponent, {
   reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
   paytoURI: parsePaytoUri(
     
"payto://iban/ES8877998399652238?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG&receiver=Sebastian",
@@ -68,7 +68,7 @@ export const WithReceiverName = 
createExample(TestedComponent, {
   exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
 
-export const Bitcoin = createExample(TestedComponent, {
+export const Bitcoin = tests.createExample(TestedComponent, {
   reservePub: "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
   paytoURI: parsePaytoUri(
     
"payto://bitcoin/bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
@@ -81,7 +81,7 @@ export const Bitcoin = createExample(TestedComponent, {
   exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
 
-export const BitcoinRegTest = createExample(TestedComponent, {
+export const BitcoinRegTest = tests.createExample(TestedComponent, {
   reservePub: "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
   paytoURI: parsePaytoUri(
     
"payto://bitcoin/bcrt1q6ps8qs6v8tkqrnru4xqqqa6rfwcx5ufpdfqht4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
@@ -93,7 +93,7 @@ export const BitcoinRegTest = createExample(TestedComponent, {
   },
   exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
-export const BitcoinTest = createExample(TestedComponent, {
+export const BitcoinTest = tests.createExample(TestedComponent, {
   reservePub: "0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
   paytoURI: parsePaytoUri(
     
"payto://bitcoin/tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
index 04b7f3e09..53bc577d4 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { SettingsView as TestedComponent } from "./Settings.js";
 
 export default {
@@ -45,7 +45,7 @@ const version = {
   },
 };
 
-export const AllOff = createExample(TestedComponent, {
+export const AllOff = tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   devModeToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
@@ -54,7 +54,7 @@ export const AllOff = createExample(TestedComponent, {
   ...version,
 });
 
-export const OneChecked = createExample(TestedComponent, {
+export const OneChecked = tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   devModeToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
@@ -63,7 +63,7 @@ export const OneChecked = createExample(TestedComponent, {
   ...version,
 });
 
-export const WithOneExchange = createExample(TestedComponent, {
+export const WithOneExchange = tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   devModeToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
@@ -85,45 +85,54 @@ export const WithOneExchange = 
createExample(TestedComponent, {
   ...version,
 });
 
-export const WithExchangeInDifferentState = createExample(TestedComponent, {
-  deviceName: "this-is-the-device-name",
-  devModeToggle: { value: false, button: {} },
-  autoOpenToggle: { value: false, button: {} },
-  clipboardToggle: { value: false, button: {} },
-  setDeviceName: () => Promise.resolve(),
-  knownExchanges: [
-    {
-      currency: "USD",
-      exchangeBaseUrl: "http://exchange1.taler";,
-      tos: {
-        currentVersion: "1",
-        acceptedVersion: "1",
-        content: "content of tos",
-        contentType: "text/plain",
-      },
-      paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"],
-    },
-    {
-      currency: "USD",
-      exchangeBaseUrl: "http://exchange2.taler";,
-      tos: {
-        currentVersion: "2",
-        acceptedVersion: "1",
-        content: "content of tos",
-        contentType: "text/plain",
+export const WithExchangeInDifferentState = tests.createExample(
+  TestedComponent,
+  {
+    deviceName: "this-is-the-device-name",
+    devModeToggle: { value: false, button: {} },
+    autoOpenToggle: { value: false, button: {} },
+    clipboardToggle: { value: false, button: {} },
+    setDeviceName: () => Promise.resolve(),
+    knownExchanges: [
+      {
+        currency: "USD",
+        exchangeBaseUrl: "http://exchange1.taler";,
+        tos: {
+          currentVersion: "1",
+          acceptedVersion: "1",
+          content: "content of tos",
+          contentType: "text/plain",
+        },
+        paytoUris: [
+          "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator",
+        ],
       },
-      paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"],
-    } as any, //TODO: complete with auditors, wireInfo and denominations
-    {
-      currency: "USD",
-      exchangeBaseUrl: "http://exchange3.taler";,
-      tos: {
-        currentVersion: "1",
-        content: "content of tos",
-        contentType: "text/plain",
+      {
+        currency: "USD",
+        exchangeBaseUrl: "http://exchange2.taler";,
+        tos: {
+          currentVersion: "2",
+          acceptedVersion: "1",
+          content: "content of tos",
+          contentType: "text/plain",
+        },
+        paytoUris: [
+          "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator",
+        ],
+      } as any, //TODO: complete with auditors, wireInfo and denominations
+      {
+        currency: "USD",
+        exchangeBaseUrl: "http://exchange3.taler";,
+        tos: {
+          currentVersion: "1",
+          content: "content of tos",
+          contentType: "text/plain",
+        },
+        paytoUris: [
+          "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator",
+        ],
       },
-      paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"],
-    },
-  ],
-  ...version,
-});
+    ],
+    ...version,
+  },
+);
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index ed1bc838a..d65f3a095 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -22,7 +22,6 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
 import { Checkbox } from "../components/Checkbox.js";
-import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js";
 import { JustInDevMode } from "../components/JustInDevMode.js";
 import { Part } from "../components/Part.js";
 import { SelectList } from "../components/SelectList.js";
@@ -34,6 +33,7 @@ import {
   SuccessText,
   WarningText,
 } from "../components/styled/index.js";
+import { useAlertContext } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useDevContext } from "../context/devContext.js";
 import { useTranslationContext } from "../context/translation.js";
@@ -50,8 +50,9 @@ const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? 
__GIT_HASH__ : undefined;
 export function SettingsPage(): VNode {
   const autoOpenToggle = useAutoOpenPermissions();
   const clipboardToggle = useClipboardPermissions();
-  const { devModeToggle } = useDevContext();
+  const { devMode, toggle } = useDevContext();
   const { name, update } = useBackupDeviceName();
+  const { pushAlertOnError } = useAlertContext();
   const webex = platform.getWalletWebExVersion();
   const api = useBackendContext();
 
@@ -72,7 +73,12 @@ export function SettingsPage(): VNode {
       setDeviceName={update}
       autoOpenToggle={autoOpenToggle}
       clipboardToggle={clipboardToggle}
-      devModeToggle={devModeToggle}
+      devModeToggle={{
+        value: devMode,
+        button: {
+          onClick: pushAlertOnError(toggle),
+        },
+      }}
       webexVersion={{
         version: webex.version,
         hash: GIT_HASH,
@@ -109,18 +115,6 @@ export function SettingsView({
   return (
     <Fragment>
       <section>
-        {autoOpenToggle.button.error && (
-          <ErrorTalerOperation
-            title={i18n.str`Could not toggle auto-open`}
-            error={autoOpenToggle.button.error.errorDetail}
-          />
-        )}
-        {/* {clipboardToggle.button.error && (
-          <ErrorTalerOperation
-            title={i18n.str`Could not toggle clipboard`}
-            error={clipboardToggle.button.error.errorDetail}
-          />
-        )} */}
         <SubTitle>
           <i18n.Translate>Navigator</i18n.Translate>
         </SubTitle>
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 868d3b0e6..bc941c9af 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -39,11 +39,13 @@ import {
   WithdrawalType,
 } from "@gnu-taler/taler-util";
 import { DevContextProviderForTesting } from "../context/devContext.js";
-import {
-  createExample,
-  createExampleWithCustomContext as createExampleInCustomContext,
-} from "../test-utils.js";
+// import {
+//   createExample,
+//   createExampleWithCustomContext as createExampleInCustomContext,
+// } from "../test-utils.js";
 import { TransactionView as TestedComponent } from "./Transaction.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import beer from "../../static-dev/beer.png";
 
 export default {
   title: "transaction details",
@@ -214,24 +216,28 @@ const transactionError = {
       hint: "The payment is too late, the offer has expired.",
     },
   },
+  when: new Date().toISOString(),
   hint: "Error: WALLET_UNEXPECTED_REQUEST_ERROR",
   message: "Unexpected error code in response",
 };
 
-export const Withdraw = createExample(TestedComponent, {
+export const Withdraw = tests.createExample(TestedComponent, {
   transaction: exampleData.withdraw,
 });
 
-export const WithdrawFiveMinutesAgo = createExample(TestedComponent, () => ({
-  transaction: {
-    ...exampleData.withdraw,
-    timestamp: TalerProtocolTimestamp.fromSeconds(
-      new Date().getTime() / 1000 - 60 * 5,
-    ),
-  },
-}));
+export const WithdrawFiveMinutesAgo = tests.createExample(
+  TestedComponent,
+  () => ({
+    transaction: {
+      ...exampleData.withdraw,
+      timestamp: TalerProtocolTimestamp.fromSeconds(
+        new Date().getTime() / 1000 - 60 * 5,
+      ),
+    },
+  }),
+);
 
-export const WithdrawFiveMinutesAgoAndPending = createExample(
+export const WithdrawFiveMinutesAgoAndPending = tests.createExample(
   TestedComponent,
   () => ({
     transaction: {
@@ -244,38 +250,41 @@ export const WithdrawFiveMinutesAgoAndPending = 
createExample(
   }),
 );
 
-export const WithdrawError = createExample(TestedComponent, {
+export const WithdrawError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.withdraw,
     error: transactionError,
   },
 });
 
-export const WithdrawErrorInDevMode = createExampleInCustomContext(
+// export const WithdrawErrorInDevMode = tests.createExampleInCustomContext(
+//   TestedComponent,
+//   {
+//     transaction: {
+//       ...exampleData.withdraw,
+//       error: transactionError,
+//     },
+//   },
+//   DevContextProviderForTesting,
+//   { value: true },
+// );
+
+export const WithdrawPendingManual = tests.createExample(
   TestedComponent,
-  {
+  () => ({
     transaction: {
       ...exampleData.withdraw,
-      error: transactionError,
+      withdrawalDetails: {
+        type: WithdrawalType.ManualTransfer,
+        exchangePaytoUris: ["payto://iban/ES8877998399652238"],
+        reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+      } as WithdrawalDetails,
+      pending: true,
     },
-  },
-  DevContextProviderForTesting,
-  { value: true },
+  }),
 );
 
-export const WithdrawPendingManual = createExample(TestedComponent, () => ({
-  transaction: {
-    ...exampleData.withdraw,
-    withdrawalDetails: {
-      type: WithdrawalType.ManualTransfer,
-      exchangePaytoUris: ["payto://iban/ES8877998399652238"],
-      reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
-    } as WithdrawalDetails,
-    pending: true,
-  },
-}));
-
-export const WithdrawPendingTalerBankUnconfirmed = createExample(
+export const WithdrawPendingTalerBankUnconfirmed = tests.createExample(
   TestedComponent,
   {
     transaction: {
@@ -291,7 +300,7 @@ export const WithdrawPendingTalerBankUnconfirmed = 
createExample(
   },
 );
 
-export const WithdrawPendingTalerBankConfirmed = createExample(
+export const WithdrawPendingTalerBankConfirmed = tests.createExample(
   TestedComponent,
   {
     transaction: {
@@ -306,18 +315,18 @@ export const WithdrawPendingTalerBankConfirmed = 
createExample(
   },
 );
 
-export const Payment = createExample(TestedComponent, {
+export const Payment = tests.createExample(TestedComponent, {
   transaction: exampleData.payment,
 });
 
-export const PaymentError = createExample(TestedComponent, {
+export const PaymentError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     error: transactionError,
   },
 });
 
-export const PaymentWithRefund = createExample(TestedComponent, {
+export const PaymentWithRefund = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
@@ -334,7 +343,7 @@ export const PaymentWithRefund = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithDeliveryDate = createExample(TestedComponent, {
+export const PaymentWithDeliveryDate = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
@@ -347,7 +356,7 @@ export const PaymentWithDeliveryDate = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithDeliveryAddr = createExample(TestedComponent, {
+export const PaymentWithDeliveryAddr = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
@@ -363,7 +372,7 @@ export const PaymentWithDeliveryAddr = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithDeliveryFull = createExample(TestedComponent, {
+export const PaymentWithDeliveryFull = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
@@ -382,7 +391,7 @@ export const PaymentWithDeliveryFull = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithRefundPending = createExample(TestedComponent, {
+export const PaymentWithRefundPending = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
@@ -392,7 +401,7 @@ export const PaymentWithRefundPending = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithFeeAndRefund = createExample(TestedComponent, {
+export const PaymentWithFeeAndRefund = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:11",
@@ -401,7 +410,7 @@ export const PaymentWithFeeAndRefund = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithFeeAndRefundFee = createExample(TestedComponent, {
+export const PaymentWithFeeAndRefundFee = tests.createExample(TestedComponent, 
{
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:11",
@@ -410,20 +419,18 @@ export const PaymentWithFeeAndRefundFee = 
createExample(TestedComponent, {
   },
 });
 
-export const PaymentWithoutFee = createExample(TestedComponent, {
+export const PaymentWithoutFee = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     amountRaw: "KUDOS:12",
   },
 });
 
-export const PaymentPending = createExample(TestedComponent, {
+export const PaymentPending = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.payment, pending: true },
 });
 
-import beer from "../../static-dev/beer.png";
-
-export const PaymentWithProducts = createExample(TestedComponent, {
+export const PaymentWithProducts = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     info: {
@@ -460,7 +467,7 @@ export const PaymentWithProducts = 
createExample(TestedComponent, {
   } as TransactionPayment,
 });
 
-export const PaymentWithLongSummary = createExample(TestedComponent, {
+export const PaymentWithLongSummary = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     info: {
@@ -484,16 +491,16 @@ export const PaymentWithLongSummary = 
createExample(TestedComponent, {
   } as TransactionPayment,
 });
 
-export const Deposit = createExample(TestedComponent, {
+export const Deposit = tests.createExample(TestedComponent, {
   transaction: exampleData.deposit,
 });
-export const DepositTalerBank = createExample(TestedComponent, {
+export const DepositTalerBank = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.deposit,
     targetPaytoUri: "payto://x-taler-bank/bank.demo.taler.net/Exchange",
   },
 });
-export const DepositBitcoin = createExample(TestedComponent, {
+export const DepositBitcoin = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.deposit,
     amountRaw: "BITCOINBTC:0.0000011",
@@ -502,88 +509,88 @@ export const DepositBitcoin = 
createExample(TestedComponent, {
       
"payto://bitcoin/bcrt1q6ps8qs6v8tkqrnru4xqqqa6rfwcx5ufpdfqht4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
   },
 });
-export const DepositIBAN = createExample(TestedComponent, {
+export const DepositIBAN = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.deposit,
     targetPaytoUri: "payto://iban/ES8877998399652238",
   },
 });
 
-export const DepositError = createExample(TestedComponent, {
+export const DepositError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.deposit,
     error: transactionError,
   },
 });
 
-export const DepositPending = createExample(TestedComponent, {
+export const DepositPending = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.deposit, pending: true },
 });
 
-export const Refresh = createExample(TestedComponent, {
+export const Refresh = tests.createExample(TestedComponent, {
   transaction: exampleData.refresh,
 });
 
-export const RefreshError = createExample(TestedComponent, {
+export const RefreshError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.refresh,
     error: transactionError,
   },
 });
 
-export const Tip = createExample(TestedComponent, {
+export const Tip = tests.createExample(TestedComponent, {
   transaction: exampleData.tip,
 });
 
-export const TipError = createExample(TestedComponent, {
+export const TipError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.tip,
     error: transactionError,
   },
 });
 
-export const TipPending = createExample(TestedComponent, {
+export const TipPending = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.tip, pending: true },
 });
 
-export const Refund = createExample(TestedComponent, {
+export const Refund = tests.createExample(TestedComponent, {
   transaction: exampleData.refund,
 });
 
-export const RefundError = createExample(TestedComponent, {
+export const RefundError = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.refund,
     error: transactionError,
   },
 });
 
-export const RefundPending = createExample(TestedComponent, {
+export const RefundPending = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.refund, pending: true },
 });
 
-export const InvoiceCreditComplete = createExample(TestedComponent, {
+export const InvoiceCreditComplete = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.pull_credit },
 });
 
-export const InvoiceCreditIncomplete = createExample(TestedComponent, {
+export const InvoiceCreditIncomplete = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.pull_credit,
     pending: true,
   },
 });
 
-export const InvoiceDebit = createExample(TestedComponent, {
+export const InvoiceDebit = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.pull_debit },
 });
 
-export const TransferCredit = createExample(TestedComponent, {
+export const TransferCredit = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.push_credit },
 });
 
-export const TransferDebitComplete = createExample(TestedComponent, {
+export const TransferDebitComplete = tests.createExample(TestedComponent, {
   transaction: { ...exampleData.push_debit },
 });
-export const TransferDebitIncomplete = createExample(TestedComponent, {
+export const TransferDebitIncomplete = tests.createExample(TestedComponent, {
   transaction: {
     ...exampleData.push_debit,
     pending: true,
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 542694490..f22f3b4ee 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -44,7 +44,7 @@ import emptyImg from "../../static/img/empty.png";
 import { Amount } from "../components/Amount.js";
 import { BankDetailsByPaytoType } from 
"../components/BankDetailsByPaytoType.js";
 import { CopyButton } from "../components/CopyButton.js";
-import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
 import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js";
 import { QR } from "../components/QR.js";
@@ -99,8 +99,8 @@ export function TransactionPage({
 
   if (state.hasError) {
     return (
-      <AlertView
-        alert={alertFromError(
+      <ErrorAlertView
+        error={alertFromError(
           i18n.str`Could not load transaction information`,
           state,
         )}
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
index 7e52d4270..4e05e1b59 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample } from "../test-utils.js";
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
 import { View as TestedComponent } from "./Welcome.js";
 
 export default {
@@ -27,7 +27,7 @@ export default {
   component: TestedComponent,
 };
 
-export const Normal = createExample(TestedComponent, {
+export const Normal = tests.createExample(TestedComponent, {
   permissionToggle: { value: true, button: {} },
   diagnostics: {
     errors: [],
@@ -38,11 +38,11 @@ export const Normal = createExample(TestedComponent, {
   },
 });
 
-export const TimedoutDiagnostics = createExample(TestedComponent, {
+export const TimedoutDiagnostics = tests.createExample(TestedComponent, {
   timedOut: true,
   permissionToggle: { value: true, button: {} },
 });
 
-export const RunningDiagnostics = createExample(TestedComponent, {
+export const RunningDiagnostics = tests.createExample(TestedComponent, {
   permissionToggle: { value: true, button: {} },
 });
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index a41372e37..0393661ac 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -93,22 +93,12 @@ export interface BackgroundApiClient {
   ): Promise<BackgroundOperations[Op]["response"]>;
 }
 
-export class WalletError extends Error {
-  public errorDetail: TalerError;
-
-  constructor(op: string, e: TalerError) {
-    super(`Wallet operation "${op}" failed: ${e.message}`);
-    this.errorDetail = e;
-    // Object.setPrototypeOf(this, WalletError.prototype);
-  }
-}
 export class BackgroundError extends Error {
   public errorDetail: TalerErrorDetail;
 
-  constructor(op: string, e: TalerErrorDetail) {
-    super(`Background operation "${op}" failed: ${e.message}`);
+  constructor(title: string, e: TalerErrorDetail) {
+    super(title);
     this.errorDetail = e;
-    // Object.setPrototypeOf(this, BackgroundError.prototype);
   }
 }
 
@@ -135,13 +125,17 @@ class BackgroundApiClientImpl implements 
BackgroundApiClient {
       if (error instanceof Error) {
         throw new BackgroundError(operation, {
           code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+          when: new Date().toISOString(),
         });
       }
       throw error;
     }
     logger.info("got response", response);
     if (response.type === "error") {
-      throw new BackgroundError(operation, response.error);
+      throw new BackgroundError(
+        `Background operation "${operation}" failed`,
+        response.error,
+      );
     }
     return response.result as any;
   }
@@ -169,8 +163,10 @@ class WalletApiClientImpl implements WalletCoreApiClient {
     }
     logger.info("got response", response);
     if (response.type === "error") {
-      const error = TalerError.fromUncheckedDetail(response.error);
-      throw new WalletError(operation, error);
+      throw new BackgroundError(
+        `Wallet operation "${operation}" failed`,
+        response.error,
+      );
     }
     return response.result as any;
   }

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