gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: add testing to web components


From: gnunet
Subject: [taler-wallet-core] branch master updated: add testing to web components
Date: Sun, 12 Jun 2022 00:10:34 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 6d06b526 add testing to web components
6d06b526 is described below

commit 6d06b52605005f4d25381fc73383c3c9e48f20f8
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sat Jun 11 19:10:26 2022 -0300

    add testing to web components
---
 packages/anastasis-webui/clean_and_build.sh        |   2 +
 packages/anastasis-webui/package.json              |   4 +-
 .../src/components/InvalidState.tsx                |  21 ++
 .../anastasis-webui/src/components/NoReducer.tsx   |  21 ++
 .../pages/home/AddingProviderScreen.stories.tsx    |  61 ----
 .../src/pages/home/AddingProviderScreen.tsx        | 367 ---------------------
 .../src/pages/home/AddingProviderScreen/index.ts   | 102 ++++++
 .../src/pages/home/AddingProviderScreen/state.ts   | 147 +++++++++
 .../pages/home/AddingProviderScreen/stories.tsx    |  89 +++++
 .../src/pages/home/AddingProviderScreen/test.ts    |  42 +++
 .../src/pages/home/AddingProviderScreen/views.tsx  | 304 +++++++++++++++++
 .../src/pages/home/AuthenticationEditorScreen.tsx  |   4 +-
 .../src/pages/home/SecretSelectionScreen.tsx       |  10 +-
 .../src/pages/home/index.storiesNo.tsx             |   2 +-
 packages/anastasis-webui/src/pages/home/index.tsx  |   6 +-
 packages/anastasis-webui/src/utils/index.tsx       |  36 +-
 .../build-fast-with-linaria.mjs                    |  17 +-
 pnpm-lock.yaml                                     |  17 +-
 18 files changed, 801 insertions(+), 451 deletions(-)

diff --git a/packages/anastasis-webui/clean_and_build.sh 
b/packages/anastasis-webui/clean_and_build.sh
index 21107a90..c0e93e47 100755
--- a/packages/anastasis-webui/clean_and_build.sh
+++ b/packages/anastasis-webui/clean_and_build.sh
@@ -37,6 +37,8 @@ echo compile
 build_css &
 build_js src/main.ts &
 build_js src/main.test.ts &
+for file in $(find src/ -name test.ts); do build_js $file; done &
+wait -n
 wait -n
 wait -n
 wait -n
diff --git a/packages/anastasis-webui/package.json 
b/packages/anastasis-webui/package.json
index 7c57160c..551ae54b 100644
--- a/packages/anastasis-webui/package.json
+++ b/packages/anastasis-webui/package.json
@@ -9,12 +9,14 @@
     "dev": "./dev.mjs",
     "prepare": "pnpm compile",
     "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
-    "test": "mocha --enable-source-maps 'dist/**/*.test.js'",
+    "test": "mocha --enable-source-maps 'dist/**/*test.js'",
     "pretty": "prettier --write src"
   },
   "dependencies": {
     "@gnu-taler/anastasis-core": "workspace:*",
     "@gnu-taler/taler-util": "workspace:*",
+    "@types/chai": "^4.3.0",
+    "chai": "^4.3.6",
     "date-fns": "2.28.0",
     "jed": "1.1.1",
     "preact": "^10.5.15",
diff --git a/packages/anastasis-webui/src/components/InvalidState.tsx 
b/packages/anastasis-webui/src/components/InvalidState.tsx
new file mode 100644
index 00000000..8e2edde5
--- /dev/null
+++ b/packages/anastasis-webui/src/components/InvalidState.tsx
@@ -0,0 +1,21 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+
+import { h, VNode } from "preact";
+
+export default function InvalidState(): VNode {
+  return <div>invalid state</div>;
+}
diff --git a/packages/anastasis-webui/src/components/NoReducer.tsx 
b/packages/anastasis-webui/src/components/NoReducer.tsx
new file mode 100644
index 00000000..550ddcca
--- /dev/null
+++ b/packages/anastasis-webui/src/components/NoReducer.tsx
@@ -0,0 +1,21 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+
+import { h, VNode } from "preact";
+
+export default function NoReducer(): VNode {
+  return <div>no reducer</div>;
+}
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx
deleted file mode 100644
index ce860ba2..00000000
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.stories.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- This file is part of GNU Anastasis
- (C) 2021-2022 Anastasis SARL
-
- GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
- terms of the GNU Affero General Public License as published by the Free 
Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
- You should have received a copy of the GNU Affero General Public License 
along with
- GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { ReducerState } from "@gnu-taler/anastasis-core";
-import { createExample, reducerStatesExample } from "../../utils/index.js";
-import { AddingProviderScreen as TestedComponent } from 
"./AddingProviderScreen.js";
-
-export default {
-  title: "Pages/ManageProvider",
-  component: TestedComponent,
-  args: {
-    order: 1,
-  },
-  argTypes: {
-    onUpdate: { action: "onUpdate" },
-    onBack: { action: "onBack" },
-  },
-};
-
-export const NewProvider = createExample(TestedComponent, {
-  ...reducerStatesExample.authEditing,
-} as ReducerState);
-
-export const NewProviderWithoutProviderList = createExample(TestedComponent, {
-  ...reducerStatesExample.authEditing,
-  authentication_providers: {},
-} as ReducerState);
-
-export const NewSmsProvider = createExample(
-  TestedComponent,
-  {
-    ...reducerStatesExample.authEditing,
-  } as ReducerState,
-  { providerType: "sms" },
-);
-
-export const NewIBANProvider = createExample(
-  TestedComponent,
-  {
-    ...reducerStatesExample.authEditing,
-  } as ReducerState,
-  { providerType: "iban" },
-);
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx
deleted file mode 100644
index 6aeee9e7..00000000
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- This file is part of GNU Anastasis
- (C) 2021-2022 Anastasis SARL
-
- GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
- terms of the GNU Affero General Public License as published by the Free 
Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
- You should have received a copy of the GNU Affero General Public License 
along with
- GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
- */
-import {
-  AuthenticationProviderStatus,
-  AuthenticationProviderStatusError,
-  AuthenticationProviderStatusOk,
-} from "@gnu-taler/anastasis-core";
-import { h, VNode } from "preact";
-import { useEffect, useRef, useState } from "preact/hooks";
-import { TextInput } from "../../components/fields/TextInput.js";
-import { useAnastasisContext } from "../../context/anastasis.js";
-import { authMethods, KnownAuthMethods } from "./authMethod/index.js";
-import { AnastasisClientFrame } from "./index.js";
-
-interface Props {
-  providerType?: KnownAuthMethods;
-  onCancel: () => void;
-}
-
-async function testProvider(
-  url: string,
-  expectedMethodType?: string,
-): Promise<void> {
-  try {
-    const response = await fetch(new URL("config", url).href);
-    const json = await response.json().catch((d) => ({}));
-    if (!("methods" in json) || !Array.isArray(json.methods)) {
-      throw Error(
-        "This provider doesn't have authentication method. Check the provider 
URL",
-      );
-    }
-    if (!expectedMethodType) {
-      return;
-    }
-    let found = false;
-    for (let i = 0; i < json.methods.length && !found; i++) {
-      found = json.methods[i].type === expectedMethodType;
-    }
-    if (!found) {
-      throw Error(
-        `This provider does not support authentication method 
${expectedMethodType}`,
-      );
-    }
-    return;
-  } catch (e) {
-    console.log("error", e);
-    const error =
-      e instanceof Error
-        ? Error(
-            `There was an error testing this provider, try another one. 
${e.message}`,
-          )
-        : Error(`There was an error testing this provider, try another one.`);
-    throw error;
-  }
-}
-
-export function AddingProviderScreen({ providerType, onCancel }: Props): VNode 
{
-  const reducer = useAnastasisContext();
-
-  const [providerURL, setProviderURL] = useState("");
-
-  const [error, setError] = useState<string | undefined>();
-  const [testing, setTesting] = useState(false);
-
-  const providerLabel = providerType
-    ? authMethods[providerType].label
-    : undefined;
-
-  const allAuthProviders =
-    !reducer ||
-    !reducer.currentReducerState ||
-    reducer.currentReducerState.reducer_type === "error" ||
-    !reducer.currentReducerState.authentication_providers
-      ? {}
-      : reducer.currentReducerState.authentication_providers;
-
-  const authProvidersByStatus = Object.keys(allAuthProviders).reduce(
-    (prev, url) => {
-      const p = allAuthProviders[url];
-      if (
-        providerLabel &&
-        p.status === "ok" &&
-        p.methods.findIndex((m) => m.type === providerType) !== -1
-      ) {
-        return prev;
-      }
-      const others = prev[p.status] ? prev[p.status] : [];
-      others.push({ ...p, url });
-      return {
-        ...prev,
-        [p.status]: others,
-      };
-    },
-    {} as Record<
-      AuthenticationProviderStatus["status"],
-      (AuthenticationProviderStatus & { url: string })[]
-    >,
-  );
-  const authProviders = authProvidersByStatus["ok"].map((p) => p.url);
-
-  console.log("rodos", allAuthProviders);
-  //FIXME: move this timeout logic into a hook
-  const timeout = useRef<number | undefined>(undefined);
-  useEffect(() => {
-    if (timeout) window.clearTimeout(timeout.current);
-    timeout.current = window.setTimeout(async () => {
-      const url = providerURL.endsWith("/") ? providerURL : providerURL + "/";
-      if (!providerURL || authProviders.includes(url)) return;
-      try {
-        setTesting(true);
-        await testProvider(url, providerType);
-        setError("");
-      } catch (e) {
-        if (e instanceof Error) setError(e.message);
-      }
-      setTesting(false);
-    }, 200);
-  }, [providerURL, reducer]);
-
-  async function addProvider(provider_url: string): Promise<void> {
-    await reducer?.transition("add_provider", { provider_url });
-    onCancel();
-  }
-  function deleteProvider(provider_url: string): void {
-    reducer?.transition("delete_provider", { provider_url });
-  }
-
-  if (!reducer) {
-    return <div>no reducer in context</div>;
-  }
-
-  if (
-    !reducer.currentReducerState ||
-    !("authentication_providers" in reducer.currentReducerState)
-  ) {
-    return <div>invalid state</div>;
-  }
-
-  let errors = !providerURL ? "Add provider URL" : undefined;
-  let url: string | undefined;
-  try {
-    url = new URL("", providerURL).href;
-  } catch {
-    errors = "Check the URL";
-  }
-  if (!!error && !errors) {
-    errors = error;
-  }
-  if (!errors && authProviders.includes(url!)) {
-    errors = "That provider is already known";
-  }
-
-  return (
-    <AnastasisClientFrame
-      hideNav
-      title="Backup: Manage providers"
-      hideNext={errors}
-    >
-      <div>
-        {!providerLabel ? (
-          <p>Add a provider url</p>
-        ) : (
-          <p>Add a provider url for a {providerLabel} service</p>
-        )}
-        <div class="container">
-          <TextInput
-            label="Provider URL"
-            placeholder="https://provider.com";
-            grabFocus
-            error={errors}
-            bind={[providerURL, setProviderURL]}
-          />
-        </div>
-        <p class="block">Example: https://kudos.demo.anastasis.lu</p>
-        {testing && <p class="has-text-info">Testing</p>}
-
-        <div
-          class="block"
-          style={{
-            marginTop: "2em",
-            display: "flex",
-            justifyContent: "space-between",
-          }}
-        >
-          <button class="button" onClick={onCancel}>
-            Cancel
-          </button>
-          <span data-tooltip={errors}>
-            <button
-              class="button is-info"
-              disabled={error !== "" || testing}
-              onClick={() => addProvider(url!)}
-            >
-              Add
-            </button>
-          </span>
-        </div>
-
-        {authProviders.length > 0 ? (
-          !providerLabel ? (
-            <p class="subtitle">Current providers</p>
-          ) : (
-            <p class="subtitle">
-              Current providers for {providerLabel} service
-            </p>
-          )
-        ) : !providerLabel ? (
-          <p class="subtitle">No known providers, add one.</p>
-        ) : (
-          <p class="subtitle">No known providers for {providerLabel} 
service</p>
-        )}
-
-        {authProviders.map((k) => {
-          const p = allAuthProviders[k] as AuthenticationProviderStatusOk;
-          return (
-            <TableRow key={k} url={k} info={p} onDelete={deleteProvider} />
-          );
-        })}
-        {authProvidersByStatus["error"]?.map((k) => {
-          const p = k as AuthenticationProviderStatusError;
-          return (
-            <TableRowError
-              key={k}
-              url={k.url}
-              info={p}
-              onDelete={deleteProvider}
-            />
-          );
-        })}
-      </div>
-    </AnastasisClientFrame>
-  );
-}
-function TableRow({
-  url,
-  info,
-  onDelete,
-}: {
-  onDelete: (s: string) => void;
-  url: string;
-  info: AuthenticationProviderStatusOk;
-}): VNode {
-  const [status, setStatus] = useState("checking");
-  useEffect(function () {
-    testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url)
-      .then(function () {
-        setStatus("responding");
-      })
-      .catch(function () {
-        setStatus("failed to contact");
-      });
-  });
-  return (
-    <div
-      class="box"
-      style={{ display: "flex", justifyContent: "space-between" }}
-    >
-      <div>
-        <div class="subtitle">{url}</div>
-        <dl>
-          <dt>
-            <b>Business Name</b>
-          </dt>
-          <dd>{info.business_name}</dd>
-          <dt>
-            <b>Supported methods</b>
-          </dt>
-          <dd>{info.methods.map((m) => m.type).join(",")}</dd>
-          <dt>
-            <b>Maximum storage</b>
-          </dt>
-          <dd>{info.storage_limit_in_megabytes} Mb</dd>
-          <dt>
-            <b>Status</b>
-          </dt>
-          <dd>{status}</dd>
-        </dl>
-      </div>
-      <div
-        class="block"
-        style={{
-          marginTop: "auto",
-          marginBottom: "auto",
-          display: "flex",
-          justifyContent: "space-between",
-          flexDirection: "column",
-        }}
-      >
-        <button class="button is-danger" onClick={() => onDelete(url)}>
-          Remove
-        </button>
-      </div>
-    </div>
-  );
-}
-
-function TableRowError({
-  url,
-  info,
-  onDelete,
-}: {
-  onDelete: (s: string) => void;
-  url: string;
-  info: AuthenticationProviderStatusError;
-}): VNode {
-  const [status, setStatus] = useState("checking");
-  useEffect(function () {
-    testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url)
-      .then(function () {
-        setStatus("responding");
-      })
-      .catch(function () {
-        setStatus("failed to contact");
-      });
-  });
-  return (
-    <div
-      class="box"
-      style={{ display: "flex", justifyContent: "space-between" }}
-    >
-      <div>
-        <div class="subtitle">{url}</div>
-        <dl>
-          <dt>
-            <b>Error</b>
-          </dt>
-          <dd>{info.hint}</dd>
-          <dt>
-            <b>Code</b>
-          </dt>
-          <dd>{info.code}</dd>
-          <dt>
-            <b>Status</b>
-          </dt>
-          <dd>{status}</dd>
-        </dl>
-      </div>
-      <div
-        class="block"
-        style={{
-          marginTop: "auto",
-          marginBottom: "auto",
-          display: "flex",
-          justifyContent: "space-between",
-          flexDirection: "column",
-        }}
-      >
-        <button class="button is-danger" onClick={() => onDelete(url)}>
-          Remove
-        </button>
-      </div>
-    </div>
-  );
-}
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts
new file mode 100644
index 00000000..5d5913ff
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts
@@ -0,0 +1,102 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+import { AuthenticationProviderStatus } from "@gnu-taler/anastasis-core";
+import InvalidState from "../../../components/InvalidState.js";
+import NoReducer from "../../../components/NoReducer.js";
+import { compose, StateViewMap } from "../../../utils/index.js";
+import useComponentState from "./state.js";
+import { WithoutProviderType, WithProviderType } from "./views.js";
+
+export type AuthProvByStatusMap = Record<
+  AuthenticationProviderStatus["status"],
+  (AuthenticationProviderStatus & { url: string })[]
+>
+
+export type State = NoReducer | InvalidState | WithType | WithoutType;
+
+export interface NoReducer {
+  status: "no-reducer";
+}
+export interface InvalidState {
+  status: "invalid-state";
+}
+
+interface CommonProps {
+  addProvider?: () => Promise<void>;
+  deleteProvider: (url: string) => Promise<void>;
+  authProvidersByStatus: AuthProvByStatusMap;
+  error: string | undefined;
+  onCancel: () => Promise<void>;
+  testing: boolean;
+  setProviderURL: (url: string) => Promise<void>;
+  providerURL: string;
+  errors: string | undefined;
+}
+
+export interface WithType extends CommonProps {
+  status: "with-type";
+  providerLabel: string;
+}
+export interface WithoutType extends CommonProps {
+  status: "without-type";
+}
+
+const map: StateViewMap<State> = {
+  "no-reducer": NoReducer,
+  "invalid-state": InvalidState,
+  "with-type": WithProviderType,
+  "without-type": WithoutProviderType,
+};
+
+export default compose("AddingProviderScreen", useComponentState, map)
+
+
+export async function testProvider(
+  url: string,
+  expectedMethodType?: string,
+): Promise<void> {
+  try {
+    const response = await fetch(new URL("config", url).href);
+    const json = await response.json().catch((d) => ({}));
+    if (!("methods" in json) || !Array.isArray(json.methods)) {
+      throw Error(
+        "This provider doesn't have authentication method. Check the provider 
URL",
+      );
+    }
+    if (!expectedMethodType) {
+      return;
+    }
+    let found = false;
+    for (let i = 0; i < json.methods.length && !found; i++) {
+      found = json.methods[i].type === expectedMethodType;
+    }
+    if (!found) {
+      throw Error(
+        `This provider does not support authentication method 
${expectedMethodType}`,
+      );
+    }
+    return;
+  } catch (e) {
+    console.log("error", e);
+    const error =
+      e instanceof Error
+        ? Error(
+          `There was an error testing this provider, try another one. 
${e.message}`,
+        )
+        : Error(`There was an error testing this provider, try another one.`);
+    throw error;
+  }
+}
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts
new file mode 100644
index 00000000..a04c7957
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts
@@ -0,0 +1,147 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+import { useEffect, useRef, useState } from "preact/hooks";
+import { useAnastasisContext } from "../../../context/anastasis.js";
+import { authMethods, KnownAuthMethods } from "../authMethod/index.jsx";
+import { AuthProvByStatusMap, State, testProvider } from "./index.js";
+
+interface Props {
+  providerType?: KnownAuthMethods;
+  onCancel: () => Promise<void>;
+}
+
+export default function useComponentState({ providerType, onCancel }: Props): 
State {
+  const reducer = useAnastasisContext();
+
+  const [providerURL, setProviderURL] = useState("");
+
+  const [error, setError] = useState<string | undefined>();
+  const [testing, setTesting] = useState(false);
+
+  const providerLabel = providerType
+    ? authMethods[providerType].label
+    : undefined;
+
+  const allAuthProviders =
+    !reducer ||
+      !reducer.currentReducerState ||
+      reducer.currentReducerState.reducer_type === "error" ||
+      !reducer.currentReducerState.authentication_providers
+      ? {}
+      : reducer.currentReducerState.authentication_providers;
+
+  const authProvidersByStatus = Object.keys(allAuthProviders).reduce(
+    (prev, url) => {
+      const p = allAuthProviders[url];
+      if (
+        providerLabel &&
+        p.status === "ok" &&
+        p.methods.findIndex((m) => m.type === providerType) !== -1
+      ) {
+        return prev;
+      }
+      prev[p.status].push({ ...p, url });
+      return prev;
+    },
+    { "not-contacted": [], disabled: [], error: [], ok: [] } as 
AuthProvByStatusMap,
+  );
+  const authProviders = authProvidersByStatus["ok"].map((p) => p.url);
+
+  //FIXME: move this timeout logic into a hook
+  const timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
+  useEffect(() => {
+    if (timeout.current) clearTimeout(timeout.current);
+    timeout.current = setTimeout(async () => {
+      const url = providerURL.endsWith("/") ? providerURL : providerURL + "/";
+      if (!providerURL || authProviders.includes(url)) return;
+      try {
+        setTesting(true);
+        await testProvider(url, providerType);
+        setError("");
+      } catch (e) {
+        if (e instanceof Error) setError(e.message);
+      }
+      setTesting(false);
+    }, 200);
+  }, [providerURL, reducer]);
+
+  if (!reducer) {
+    return {
+      status: "no-reducer",
+    };
+  }
+
+  if (
+    !reducer.currentReducerState ||
+    !("authentication_providers" in reducer.currentReducerState)
+  ) {
+    return {
+      status: "invalid-state",
+    };
+  }
+
+  const addProvider = async (provider_url: string): Promise<void> => {
+    await reducer.transition("add_provider", { provider_url });
+    onCancel();
+  }
+  const deleteProvider = async (provider_url: string): Promise<void> => {
+    reducer.transition("delete_provider", { provider_url });
+  }
+
+  let errors = !providerURL ? "Add provider URL" : undefined;
+  let url: string | undefined;
+  try {
+    url = new URL("", providerURL).href;
+  } catch {
+    errors = "Check the URL";
+  }
+  const _url = url
+
+  if (!!error && !errors) {
+    errors = error;
+  }
+  if (!errors && authProviders.includes(url!)) {
+    errors = "That provider is already known";
+  }
+
+  const commonState = {
+    addProvider: !_url ? undefined : async () => addProvider(_url),
+    deleteProvider: async (url: string) => deleteProvider(url),
+    allAuthProviders,
+    authProvidersByStatus,
+    onCancel,
+    providerURL,
+    testing,
+    setProviderURL: async (s: string) => setProviderURL(s),
+    errors,
+    error,
+  }
+
+  if (!providerLabel) {
+    return {
+      status: "without-type",
+      ...commonState
+    }
+  } else {
+    return {
+      status: "with-type",
+      providerLabel,
+      ...commonState
+    }
+  }
+
+}
+
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
new file mode 100644
index 00000000..6d8a634d
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
@@ -0,0 +1,89 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { AuthenticationProviderStatusOk } from "@gnu-taler/anastasis-core";
+import { createExampleWithoutAnastasis } from "../../../utils/index.jsx";
+import { WithoutProviderType, WithProviderType } from "./views.jsx";
+
+export default {
+  title: "Pages/ManageProvider",
+  args: {
+    order: 1,
+  },
+  argTypes: {
+    onUpdate: { action: "onUpdate" },
+    onBack: { action: "onBack" },
+  },
+};
+
+export const NewProvider = createExampleWithoutAnastasis(WithoutProviderType, {
+  authProvidersByStatus: {
+    ok: [
+      {
+        business_name: "X provider",
+        status: "ok",
+        storage_limit_in_megabytes: 5,
+        methods: [
+          {
+            type: "question",
+            usage_fee: "KUDOS:1",
+          },
+        ],
+        url: "",
+      } as AuthenticationProviderStatusOk & { url: string },
+    ],
+    "not-contacted": [],
+    disabled: [],
+    error: [],
+  },
+});
+
+export const NewProviderWithoutProviderList = createExampleWithoutAnastasis(
+  WithoutProviderType,
+  {
+    authProvidersByStatus: {
+      ok: [],
+      "not-contacted": [],
+      disabled: [],
+      error: [],
+    },
+  },
+);
+
+export const NewSmsProvider = createExampleWithoutAnastasis(WithProviderType, {
+  authProvidersByStatus: {
+    ok: [],
+    "not-contacted": [],
+    disabled: [],
+    error: [],
+  },
+  providerLabel: "sms",
+});
+
+export const NewIBANProvider = createExampleWithoutAnastasis(WithProviderType, 
{
+  authProvidersByStatus: {
+    ok: [],
+    "not-contacted": [],
+    disabled: [],
+    error: [],
+  },
+  providerLabel: "IBAN",
+});
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/test.ts 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/test.ts
new file mode 100644
index 00000000..d051d7c0
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/test.ts
@@ -0,0 +1,42 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { expect } from "chai";
+import { mountHook } from "../../../test-utils.js";
+import useComponentState from "./state.js";
+
+describe("AddingProviderScreen states", () => {
+  it("should have status 'no-balance' when balance is empty", async () => {
+    const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } =
+      mountHook(() =>
+        useComponentState({ onCancel: async () => { null } }),
+      );
+
+    {
+      const { status } = getLastResultOrThrow();
+      expect(status).equal("no-reducer");
+    }
+
+    await assertNoPendingUpdate();
+
+  });
+
+});
diff --git 
a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx 
b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
new file mode 100644
index 00000000..bb1283a8
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
@@ -0,0 +1,304 @@
+/*
+ This file is part of GNU Anastasis
+ (C) 2021-2022 Anastasis SARL
+
+ GNU Anastasis is free software; you can redistribute it and/or modify it 
under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT 
ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+ You should have received a copy of the GNU Affero General Public License 
along with
+ GNU Anastasis; see the file COPYING.  If not, see 
<http://www.gnu.org/licenses/>
+ */
+import {
+  AuthenticationProviderStatusError,
+  AuthenticationProviderStatusOk,
+} from "@gnu-taler/anastasis-core";
+import { h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { TextInput } from "../../../components/fields/TextInput.js";
+import { AnastasisClientFrame } from "../index.js";
+import { testProvider, WithoutType, WithType } from "./index.js";
+
+export function WithProviderType(props: WithType): VNode {
+  return (
+    <AnastasisClientFrame
+      hideNav
+      title="Backup: Manage providers1"
+      hideNext={props.errors}
+    >
+      <div>
+        <p>Add a provider url for a {props.providerLabel} service</p>
+        <div class="container">
+          <TextInput
+            label="Provider URL"
+            placeholder="https://provider.com";
+            grabFocus
+            error={props.errors}
+            bind={[props.providerURL, props.setProviderURL]}
+          />
+        </div>
+        <p class="block">Example: https://kudos.demo.anastasis.lu</p>
+        {props.testing && <p class="has-text-info">Testing</p>}
+
+        <div
+          class="block"
+          style={{
+            marginTop: "2em",
+            display: "flex",
+            justifyContent: "space-between",
+          }}
+        >
+          <button class="button" onClick={props.onCancel}>
+            Cancel
+          </button>
+          <span data-tooltip={props.errors}>
+            <button
+              class="button is-info"
+              disabled={props.error !== "" || props.testing}
+              onClick={props.addProvider}
+            >
+              Add
+            </button>
+          </span>
+        </div>
+
+        {props.authProvidersByStatus["ok"].length > 0 ? (
+          <p class="subtitle">
+            Current providers for {props.providerLabel} service
+          </p>
+        ) : (
+          <p class="subtitle">
+            No known providers for {props.providerLabel} service
+          </p>
+        )}
+
+        {props.authProvidersByStatus["ok"].map((k, i) => {
+          const p = k as AuthenticationProviderStatusOk;
+          return (
+            <TableRow
+              key={i}
+              url={k.url}
+              info={p}
+              onDelete={props.deleteProvider}
+            />
+          );
+        })}
+        <p class="subtitle">Providers with errors</p>
+        {props.authProvidersByStatus["error"].map((k, i) => {
+          const p = k as AuthenticationProviderStatusError;
+          return (
+            <TableRowError
+              key={i}
+              url={k.url}
+              info={p}
+              onDelete={props.deleteProvider}
+            />
+          );
+        })}
+      </div>
+    </AnastasisClientFrame>
+  );
+}
+
+export function WithoutProviderType(props: WithoutType): VNode {
+  return (
+    <AnastasisClientFrame
+      hideNav
+      title="Backup: Manage providers2"
+      hideNext={props.errors}
+    >
+      <div>
+        <p>Add a provider url</p>
+        <div class="container">
+          <TextInput
+            label="Provider URL"
+            placeholder="https://provider.com";
+            grabFocus
+            error={props.errors}
+            bind={[props.providerURL, props.setProviderURL]}
+          />
+        </div>
+        <p class="block">Example: https://kudos.demo.anastasis.lu</p>
+        {props.testing && <p class="has-text-info">Testing</p>}
+
+        <div
+          class="block"
+          style={{
+            marginTop: "2em",
+            display: "flex",
+            justifyContent: "space-between",
+          }}
+        >
+          <button class="button" onClick={props.onCancel}>
+            Cancel
+          </button>
+          <span data-tooltip={props.errors}>
+            <button
+              class="button is-info"
+              disabled={props.error !== "" || props.testing}
+              onClick={props.addProvider}
+            >
+              Add
+            </button>
+          </span>
+        </div>
+
+        {props.authProvidersByStatus["ok"].length > 0 ? (
+          <p class="subtitle">Current providers</p>
+        ) : (
+          <p class="subtitle">No known providers, add one.</p>
+        )}
+
+        {props.authProvidersByStatus["ok"].map((k, i) => {
+          const p = k as AuthenticationProviderStatusOk;
+          return (
+            <TableRow
+              key={i}
+              url={k.url}
+              info={p}
+              onDelete={props.deleteProvider}
+            />
+          );
+        })}
+        <p class="subtitle">Providers with errors</p>
+        {props.authProvidersByStatus["error"].map((k, i) => {
+          const p = k as AuthenticationProviderStatusError;
+          return (
+            <TableRowError
+              key={i}
+              url={k.url}
+              info={p}
+              onDelete={props.deleteProvider}
+            />
+          );
+        })}
+      </div>
+    </AnastasisClientFrame>
+  );
+}
+
+function TableRow({
+  url,
+  info,
+  onDelete,
+}: {
+  onDelete: (s: string) => Promise<void>;
+  url: string;
+  info: AuthenticationProviderStatusOk;
+}): VNode {
+  const [status, setStatus] = useState("checking");
+  useEffect(function () {
+    testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url)
+      .then(function () {
+        setStatus("responding");
+      })
+      .catch(function () {
+        setStatus("failed to contact");
+      });
+  });
+  return (
+    <div
+      class="box"
+      style={{ display: "flex", justifyContent: "space-between" }}
+    >
+      <div>
+        <div class="subtitle">{url}</div>
+        <dl>
+          <dt>
+            <b>Business Name</b>
+          </dt>
+          <dd>{info.business_name}</dd>
+          <dt>
+            <b>Supported methods</b>
+          </dt>
+          <dd>{info.methods.map((m) => m.type).join(",")}</dd>
+          <dt>
+            <b>Maximum storage</b>
+          </dt>
+          <dd>{info.storage_limit_in_megabytes} Mb</dd>
+          <dt>
+            <b>Status</b>
+          </dt>
+          <dd>{status}</dd>
+        </dl>
+      </div>
+      <div
+        class="block"
+        style={{
+          marginTop: "auto",
+          marginBottom: "auto",
+          display: "flex",
+          justifyContent: "space-between",
+          flexDirection: "column",
+        }}
+      >
+        <button class="button is-danger" onClick={() => onDelete(url)}>
+          Remove
+        </button>
+      </div>
+    </div>
+  );
+}
+
+function TableRowError({
+  url,
+  info,
+  onDelete,
+}: {
+  onDelete: (s: string) => void;
+  url: string;
+  info: AuthenticationProviderStatusError;
+}): VNode {
+  const [status, setStatus] = useState("checking");
+  useEffect(function () {
+    testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url)
+      .then(function () {
+        setStatus("responding");
+      })
+      .catch(function () {
+        setStatus("failed to contact");
+      });
+  });
+  return (
+    <div
+      class="box"
+      style={{ display: "flex", justifyContent: "space-between" }}
+    >
+      <div>
+        <div class="subtitle">{url}</div>
+        <dl>
+          <dt>
+            <b>Error</b>
+          </dt>
+          <dd>{info.hint}</dd>
+          <dt>
+            <b>Code</b>
+          </dt>
+          <dd>{info.code}</dd>
+          <dt>
+            <b>Status</b>
+          </dt>
+          <dd>{status}</dd>
+        </dl>
+      </div>
+      <div
+        class="block"
+        style={{
+          marginTop: "auto",
+          marginBottom: "auto",
+          display: "flex",
+          justifyContent: "space-between",
+          flexDirection: "column",
+        }}
+      >
+        <button class="button is-danger" onClick={() => onDelete(url)}>
+          Remove
+        </button>
+      </div>
+    </div>
+  );
+}
diff --git 
a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx 
b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
index f93ecfd8..3018f88d 100644
--- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
@@ -17,7 +17,7 @@ import { AuthMethod, ReducerStateBackup } from 
"@gnu-taler/anastasis-core";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { useAnastasisContext } from "../../context/anastasis.js";
-import { AddingProviderScreen } from "./AddingProviderScreen.js";
+import AddingProviderScreen from "./AddingProviderScreen/index.js";
 import {
   authMethods,
   AuthMethodSetupProps,
@@ -84,7 +84,7 @@ export function AuthenticationEditorScreen(): VNode {
   if (manageProvider !== undefined) {
     return (
       <AddingProviderScreen
-        onCancel={() => setManageProvider(undefined)}
+        onCancel={async () => setManageProvider(undefined)}
         providerType={
           isKnownAuthMethods(manageProvider) ? manageProvider : undefined
         }
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx 
b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
index 7d92bcd2..11271aaa 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
@@ -22,7 +22,7 @@ import { useEffect, useState } from "preact/hooks";
 import { AsyncButton } from "../../components/AsyncButton.js";
 import { PhoneNumberInput } from "../../components/fields/NumberInput.js";
 import { useAnastasisContext } from "../../context/anastasis.js";
-import { AddingProviderScreen } from "./AddingProviderScreen.js";
+import AddingProviderScreen from "./AddingProviderScreen/index.js";
 import { AnastasisClientFrame } from "./index.js";
 
 export function SecretSelectionScreen(): VNode {
@@ -54,7 +54,9 @@ export function SecretSelectionScreen(): VNode {
   const recoveryDocument = reducer.currentReducerState.recovery_document;
 
   if (manageProvider) {
-    return <AddingProviderScreen onCancel={() => setManageProvider(false)} />;
+    return (
+      <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+    );
   }
 
   if (reducer.discoveryState.state === "none") {
@@ -220,7 +222,9 @@ export function OldSecretSelectionScreen(): VNode {
   }
 
   if (manageProvider) {
-    return <AddingProviderScreen onCancel={() => setManageProvider(false)} />;
+    return (
+      <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+    );
   }
 
   const providerInfo = provs[
diff --git a/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx 
b/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
index 1c47b57e..0d01a5c8 100644
--- a/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
+++ b/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import * as a23 from "./AddingProviderScreen.stories.js";
+import * as a23 from "./AddingProviderScreen/stories.js";
 import * as a28 from "./AttributeEntryScreen.stories.js";
 import * as a18 from "./AuthenticationEditorScreen.stories.js";
 import * as a8 from "./authMethod/AuthMethodEmailSetup.stories.js";
diff --git a/packages/anastasis-webui/src/pages/home/index.tsx 
b/packages/anastasis-webui/src/pages/home/index.tsx
index 57f935bd..25d9c63d 100644
--- a/packages/anastasis-webui/src/pages/home/index.tsx
+++ b/packages/anastasis-webui/src/pages/home/index.tsx
@@ -168,9 +168,9 @@ export function AnastasisClientFrame(props: 
AnastasisClientFrameProps): VNode {
       window.removeEventListener("popstate", browserOnBackButton);
     };
   }, []);
-  if (!reducer) {
-    return <p>Fatal: Reducer must be in context.</p>;
-  }
+  // if (!reducer) {
+  //   return <p>Fatal: Reducer must be in context.</p>;
+  // }
 
   return (
     <Fragment>
diff --git a/packages/anastasis-webui/src/utils/index.tsx 
b/packages/anastasis-webui/src/utils/index.tsx
index 204c48d1..63bed939 100644
--- a/packages/anastasis-webui/src/utils/index.tsx
+++ b/packages/anastasis-webui/src/utils/index.tsx
@@ -21,13 +21,26 @@ import {
   ReducerState,
   ReducerStateRecovery,
 } from "@gnu-taler/anastasis-core";
-import { FunctionalComponent, h, VNode } from "preact";
+import { ComponentChildren, FunctionalComponent, h, VNode } from "preact";
 import { AnastasisProvider } from "../context/anastasis.js";
 
 const noop = async (): Promise<void> => {
   return;
 };
 
+export function createExampleWithoutAnastasis<Props>(
+  Component: FunctionalComponent<Props>,
+  props: Partial<Props> | (() => Partial<Props>),
+): ComponentChildren {
+  //FIXME: props are evaluated on build time
+  // in some cases we want to evaluated the props on render time so we can get 
some relative timestamp
+  // check how we can build evaluatedProps in render time
+  const evaluatedProps = typeof props === "function" ? props() : props;
+  const Render = (args: any): VNode => h(Component, args);
+  Render.args = evaluatedProps;
+  return Render;
+}
+
 export function createExample<Props>(
   Component: FunctionalComponent<Props>,
   currentReducerState?: ReducerState,
@@ -293,3 +306,24 @@ export const reducerStatesExample = {
     backup_state: BackupStates.TruthsPaying,
   } as ReducerState,
 };
+
+export type StateFunc<S> = (p: S) => VNode;
+
+export type StateViewMap<StateType extends { status: string }> = {
+  [S in StateType as S["status"]]: StateFunc<S>;
+};
+
+export function compose<SType extends { status: string }, PType>(
+  name: string,
+  hook: (p: PType) => SType,
+  vs: StateViewMap<SType>,
+): (p: PType) => VNode {
+  const Component = (p: PType): VNode => {
+    const state = hook(p);
+    const s = state.status as unknown as SType["status"];
+    const c = vs[s] as unknown as StateFunc<SType>;
+    return c(state);
+  };
+  Component.name = `${name}`;
+  return Component;
+}
diff --git a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs 
b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
index 41747a74..177c1761 100755
--- a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
+++ b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
@@ -1,4 +1,19 @@
 #!/usr/bin/env node
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
 
 import linaria from '@linaria/esbuild'
 import esbuild from 'esbuild'
@@ -61,7 +76,7 @@ export const buildConfig = {
   ],
   format: 'iife',
   platform: 'browser',
-  sourcemap: true, 
+  sourcemap: true,
   jsxFactory: 'h',
   jsxFragment: 'Fragment',
   // define: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7e358f62..190746c9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -56,10 +56,12 @@ importers:
       '@creativebulma/bulma-tooltip': ^1.2.0
       '@gnu-taler/anastasis-core': workspace:*
       '@gnu-taler/taler-util': workspace:*
+      '@types/chai': ^4.3.0
       '@types/mocha': ^9.0.0
       bulma: ^0.9.3
       bulma-checkbox: ^1.1.1
       bulma-radio: ^1.1.1
+      chai: ^4.3.6
       chokidar: ^3.5.3
       date-fns: 2.28.0
       eslint-plugin-header: ^3.1.1
@@ -76,6 +78,8 @@ importers:
     dependencies:
       '@gnu-taler/anastasis-core': link:../anastasis-core
       '@gnu-taler/taler-util': link:../taler-util
+      '@types/chai': 4.3.0
+      chai: 4.3.6
       date-fns: 2.28.0
       jed: 1.1.1
       preact: 10.6.5
@@ -3488,7 +3492,6 @@ packages:
 
   /@types/chai/4.3.0:
     resolution: {integrity: 
sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==}
-    dev: true
 
   /@types/chrome/0.0.176:
     resolution: {integrity: 
sha512-LOveFOMIUhMJjvRzZv5whGBpncP/gdJ4hcxeAqg94wGi6CyKaCmLgFSofgItf85GuLTl/0BQ6J/Y1e8BqZWfEg==}
@@ -4330,7 +4333,6 @@ packages:
 
   /assertion-error/1.1.0:
     resolution: {integrity: 
sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
-    dev: true
 
   /assign-symbols/1.0.0:
     resolution: {integrity: sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=}
@@ -5208,7 +5210,6 @@ packages:
       loupe: 2.3.4
       pathval: 1.1.1
       type-detect: 4.0.8
-    dev: true
 
   /chalk/0.4.0:
     resolution: {integrity: sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=}
@@ -5264,8 +5265,7 @@ packages:
     dev: true
 
   /check-error/1.0.2:
-    resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=}
-    dev: true
+    resolution: {integrity: 
sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
 
   /chokidar/2.1.8:
     resolution: {integrity: 
sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==}
@@ -6242,7 +6242,6 @@ packages:
     engines: {node: '>=0.12'}
     dependencies:
       type-detect: 4.0.8
-    dev: true
 
   /deep-equal/1.1.1:
     resolution: {integrity: 
sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==}
@@ -7773,8 +7772,7 @@ packages:
     dev: true
 
   /get-func-name/2.0.0:
-    resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=}
-    dev: true
+    resolution: {integrity: 
sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
 
   /get-intrinsic/1.1.1:
     resolution: {integrity: 
sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
@@ -9312,7 +9310,6 @@ packages:
     resolution: {integrity: 
sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==}
     dependencies:
       get-func-name: 2.0.0
-    dev: true
 
   /lower-case/1.1.4:
     resolution: {integrity: sha1-miyr0bno4K6ZOkv31YdcOcQujqw=}
@@ -10453,7 +10450,6 @@ packages:
 
   /pathval/1.1.1:
     resolution: {integrity: 
sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
-    dev: true
 
   /pbkdf2/3.1.2:
     resolution: {integrity: 
sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
@@ -13165,7 +13161,6 @@ packages:
   /type-detect/4.0.8:
     resolution: {integrity: 
sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
     engines: {node: '>=4'}
-    dev: true
 
   /type-fest/0.13.1:
     resolution: {integrity: 
sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}

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