gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (efb5bf9de -> 4a781bd0d)


From: gnunet
Subject: [taler-wallet-core] branch master updated (efb5bf9de -> 4a781bd0d)
Date: Tue, 10 Jan 2023 00:20:19 +0100

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

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

    from efb5bf9de fix broken compile
     new 8a70edb2f add 'when' to error-detail and remove error as normal 
response when doing backup
     new 4a781bd0d fix #7153: more error handling

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/taler-util/src/wallet-types.ts            |   2 +
 .../src/crypto/workers/crypto-dispatcher.test.ts   |   1 +
 packages/taler-wallet-core/src/errors.ts           |   6 +-
 .../src/operations/backup/index.ts                 |  23 +---
 .../taler-wallet-core/src/operations/balance.ts    |   2 +
 .../src/operations/pay-merchant.ts                 |   3 +
 .../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 ++--
 97 files changed, 1016 insertions(+), 1024 deletions(-)

diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 06edfe285..367d9c4c9 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -249,6 +249,7 @@ export interface ConfirmPayResultPending {
 export const codecForTalerErrorDetail = (): Codec<TalerErrorDetail> =>
   buildCodecForObject<TalerErrorDetail>()
     .property("code", codecForNumber())
+    .property("when", codecForString())
     .property("hint", codecOptional(codecForString()))
     .build("TalerErrorDetail");
 
@@ -537,6 +538,7 @@ export interface WalletDiagnostics {
 
 export interface TalerErrorDetail {
   code: TalerErrorCode;
+  when: string;
   hint?: string;
   [x: string]: unknown;
 }
diff --git 
a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts 
b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
index df2e645a7..ca4a79d20 100644
--- a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
@@ -74,6 +74,7 @@ export class MyCryptoWorker implements CryptoWorker {
           type: "error",
           error: {
             code: 42,
+            when: "now",
             hint: "bla",
           },
         };
diff --git a/packages/taler-wallet-core/src/errors.ts 
b/packages/taler-wallet-core/src/errors.ts
index 37a31a8aa..308e9c7a8 100644
--- a/packages/taler-wallet-core/src/errors.ts
+++ b/packages/taler-wallet-core/src/errors.ts
@@ -103,7 +103,8 @@ export function makeErrorDetail<C extends TalerErrorCode>(
   if (!hint && !(detail as any).hint) {
     hint = getDefaultHint(code);
   }
-  return { code, hint, ...detail };
+  const when = new Date().toISOString();
+  return { code, when, hint, ...detail };
 }
 
 export function makePendingOperationFailedError(
@@ -160,7 +161,8 @@ export class TalerError<T = any> extends Error {
     if (!hint) {
       hint = getDefaultHint(code);
     }
-    return new TalerError<unknown>({ code, hint, ...detail });
+    const when = new Date().toISOString();
+    return new TalerError<unknown>({ code, when, hint, ...detail });
   }
 
   static fromUncheckedDetail(d: TalerErrorDetail): TalerError {
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index a44e8f55a..27d27da0d 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -627,8 +627,7 @@ export const codecForAddBackupProviderRequest =
 
 export type AddBackupProviderResponse =
   | AddBackupProviderOk
-  | AddBackupProviderPaymentRequired
-  | AddBackupProviderError;
+  | AddBackupProviderPaymentRequired;
 
 interface AddBackupProviderOk {
   status: "ok";
@@ -637,10 +636,6 @@ interface AddBackupProviderPaymentRequired {
   status: "payment-required";
   talerUri?: string;
 }
-interface AddBackupProviderError {
-  status: "error";
-  error: TalerErrorDetail;
-}
 
 export const codecForAddBackupProviderOk = (): Codec<AddBackupProviderOk> =>
   buildCodecForObject<AddBackupProviderOk>()
@@ -654,13 +649,6 @@ export const codecForAddBackupProviderPaymenrRequired =
       .property("talerUri", codecOptional(codecForString()))
       .build("AddBackupProviderPaymentRequired");
 
-export const codecForAddBackupProviderError =
-  (): Codec<AddBackupProviderError> =>
-    buildCodecForObject<AddBackupProviderError>()
-      .property("status", codecForConstString("error"))
-      .property("error", codecForTalerErrorDetail())
-      .build("AddBackupProviderError");
-
 export const codecForAddBackupProviderResponse =
   (): Codec<AddBackupProviderResponse> =>
     buildCodecForUnion<AddBackupProviderResponse>()
@@ -670,7 +658,6 @@ export const codecForAddBackupProviderResponse =
         "payment-required",
         codecForAddBackupProviderPaymenrRequired(),
       )
-      .alternative("error", codecForAddBackupProviderError())
       .build("AddBackupProviderResponse");
 
 export async function addBackupProvider(
@@ -745,10 +732,10 @@ async function runFirstBackupCycleForProvider(
   const resp = await runBackupCycleForProvider(ws, args);
   switch (resp.type) {
     case OperationAttemptResultType.Error:
-      return {
-        status: "error",
-        error: resp.errorDetail,
-      };
+      throw TalerError.fromDetail(
+        TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
+        resp.errorDetail,
+      );
     case OperationAttemptResultType.Finished:
       return {
         status: "ok",
diff --git a/packages/taler-wallet-core/src/operations/balance.ts 
b/packages/taler-wallet-core/src/operations/balance.ts
index 383aa5dc1..2c57f8af8 100644
--- a/packages/taler-wallet-core/src/operations/balance.ts
+++ b/packages/taler-wallet-core/src/operations/balance.ts
@@ -55,6 +55,7 @@ import {
   ExchangeHandle,
   canonicalizeBaseUrl,
   parsePaytoUri,
+  TalerErrorCode,
 } from "@gnu-taler/taler-util";
 import {
   AllowedAuditorInfo,
@@ -66,6 +67,7 @@ import { GetReadOnlyAccess } from "../util/query.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { getExchangeDetails } from "./exchanges.js";
 import { checkLogicInvariant } from "../util/invariants.js";
+import { TalerError } from "../errors.js";
 
 /**
  * Logger.
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 6026e0860..570a50c47 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -1824,6 +1824,7 @@ export async function processPurchase(
       errorDetail: {
         // FIXME: allocate more specific error code
         code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
+        when: new Date().toISOString(),
         hint: `trying to pay for purchase that is not in the database`,
         proposalId: proposalId,
       },
@@ -1874,6 +1875,7 @@ export async function processPurchasePay(
       errorDetail: {
         // FIXME: allocate more specific error code
         code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
+        when: new Date().toISOString(),
         hint: `trying to pay for purchase that is not in the database`,
         proposalId: proposalId,
       },
@@ -1996,6 +1998,7 @@ export async function processPurchasePay(
 
           await scheduleRetry(ws, RetryTags.forPay(purchase), {
             code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
+            when: new Date().toISOString(),
             message: "unexpected exception",
             hint: "unexpected exception",
             details: {
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]