diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/AddBackupProvider')
5 files changed, 113 insertions, 145 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts index 3205588af..daa6b425d 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts @@ -15,29 +15,22 @@ */ import { - AmountJson, - BackupBackupProviderTerms, + SyncTermsOfServiceResponse, TalerErrorDetail, } from "@gnu-taler/taler-util"; -import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core"; +import { ErrorAlertView } from "../../components/CurrentAlerts.js"; import { Loading } from "../../components/Loading.js"; -import { HookError } from "../../hooks/useAsyncAsHook.js"; +import { ErrorAlert } from "../../context/alert.js"; import { ButtonHandler, TextFieldHandler, ToggleHandler, } from "../../mui/handlers.js"; -import { compose, StateViewMap } from "../../utils/index.js"; -import { wxApi } from "../../wxApi.js"; +import { StateViewMap, compose } from "../../utils/index.js"; import { useComponentState } from "./state.js"; -import { - LoadingUriView, - SelectProviderView, - ConfirmProviderView, -} from "./views.js"; +import { ConfirmProviderView, SelectProviderView } from "./views.js"; export interface Props { - currency: string; onBack: () => Promise<void>; onComplete: (pid: string) => Promise<void>; onPaymentRequired: (uri: string) => Promise<void>; @@ -56,13 +49,13 @@ export namespace State { } export interface LoadingUriError { - status: "loading-error"; - error: HookError; + status: "error"; + error: ErrorAlert; } export interface ConfirmProvider { status: "confirm-provider"; - error: undefined | TalerErrorDetail; + error: undefined; url: string; provider: SyncTermsOfServiceResponse; tos: ToggleHandler; @@ -83,13 +76,13 @@ export namespace State { const viewMapping: StateViewMap<State> = { loading: Loading, - "loading-error": LoadingUriView, + error: ErrorAlertView, "select-provider": SelectProviderView, "confirm-provider": ConfirmProviderView, }; export const AddBackupProviderPage = compose( "AddBackupProvider", - (p: Props) => useComponentState(p, wxApi), + (p: Props) => useComponentState(p), viewMapping, ); diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts index 504ee4678..75b8e53c0 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts @@ -17,15 +17,13 @@ import { canonicalizeBaseUrl, Codec, - TalerErrorDetail, -} from "@gnu-taler/taler-util"; -import { codecForSyncTermsOfServiceResponse, - WalletApiOperation, -} from "@gnu-taler/taler-wallet-core"; +} from "@gnu-taler/taler-util"; +import { 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 { wxApi } from "../../wxApi.js"; import { Props, State } from "./index.js"; type UrlState<T> = UrlOk<T> | UrlError; @@ -101,66 +99,69 @@ function useUrlState<T>( } const constHref = href; - useDebounceEffect( - 500, - constHref == undefined - ? undefined - : async () => { - const req = await fetch(constHref).catch((e) => { - return setState({ - status: "network-error", - href: constHref, - }); - }); - if (!req) return; + async function checkURL() { + if (!constHref) { + return; + } + const req = await fetch(constHref).catch((e) => { + return setState({ + status: "network-error", + href: constHref, + }); + }); + if (!req) return; - if (req.status >= 400 && req.status < 500) { - setState({ - status: "client-error", - code: req.status, - }); - return; - } - if (req.status > 500) { - setState({ - status: "server-error", - code: req.status, - }); - return; - } + if (req.status >= 400 && req.status < 500) { + setState({ + status: "client-error", + code: req.status, + }); + return; + } + if (req.status > 500) { + setState({ + status: "server-error", + code: req.status, + }); + return; + } - const json = await req.json(); - try { - const result = codec.decode(json); - setState({ status: "ok", result }); - } catch (e: any) { - setState({ status: "parsing-error", json }); - } - }, + const json = await req.json(); + try { + const result = codec.decode(json); + setState({ status: "ok", result }); + } catch (e: any) { + setState({ status: "parsing-error", json }); + } + } + + useDebounceEffect( + 500, + constHref == undefined ? undefined : checkURL, [host, path], ); return state; } -export function useComponentState( - { currency, onBack, onComplete, onPaymentRequired }: Props, - api: typeof wxApi, -): State { +export function useComponentState({ + onBack, + onComplete, + onPaymentRequired, +}: Props): State { + const api = useBackendContext(); 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, { @@ -176,8 +177,6 @@ export function useComponentState( } else { return onComplete(url); } - case "error": - return setOperationError(resp.error); case "ok": return onComplete(url); default: @@ -188,18 +187,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 ?? "", @@ -211,25 +210,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..7ac92c6c9 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx @@ -19,17 +19,18 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import * as tests from "@gnu-taler/web-util/testing"; import { ConfirmProviderView, SelectProviderView } from "./views.js"; +import { AmountString } from "@gnu-taler/taler-util"; 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", + annual_fee: "KUDOS:0.1" as AmountString, storage_limit_in_megabytes: 20, version: "1", }, @@ -40,10 +41,10 @@ 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", + annual_fee: "ARS:0" as AmountString, storage_limit_in_megabytes: 20, version: "1", }, @@ -54,14 +55,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 +73,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 +85,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 +97,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 1143853f8..058f4f460 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts @@ -19,61 +19,50 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; -import { - createWalletApiMock, - mountHook, - nullFunction, -} from "../../test-utils.js"; +import * as tests from "@gnu-taler/web-util/testing"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { Props } from "./index.js"; import { useComponentState } from "./state.js"; const props: Props = { - currency: "KUDOS", onBack: nullFunction, onComplete: nullFunction, onPaymentRequired: nullFunction, }; describe("AddBackupProvider states", () => { - it("should start in 'select-provider' state", async () => { - const { handler, mock } = createWalletApiMock(); + /** + * FIXME: this test has inconsistent behavior. + * it should always expect one state but for some reason + * (maybe race condition) it sometime expect 1 update when + * it should no update + */ + it.skip("should start in 'select-provider' state", async () => { + const { handler, TestingContext } = createWalletApiMock(); - // handler.addWalletCallResponse( - // WalletApiOperation.ListKnownBankAccounts, - // undefined, - // { - // accounts: [], - // }, - // ); + const hookBehavior = await tests.hookBehaveLikeThis( + useComponentState, + props, + [ + (state) => { + expect(state.status).equal("select-provider"); + if (state.status !== "select-provider") return; + expect(state.name.value).eq(""); + expect(state.url.value).eq(""); + }, + //FIXME: this shouldn't take 2 updates, just + // (state) => { + // expect(state.status).equal("select-provider"); + // if (state.status !== "select-provider") return; + // expect(state.name.value).eq(""); + // expect(state.url.value).eq(""); + // }, + ], + TestingContext, + ); - const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } = - mountHook(() => useComponentState(props, mock)); - - { - const state = pullLastResultOrThrow(); - expect(state.status).equal("select-provider"); - if (state.status !== "select-provider") return; - expect(state.name.value).eq(""); - expect(state.url.value).eq(""); - } - - //FIXME: this should not make an extra update - /** - * this may be due to useUrlState because is using an effect over - * a dependency with a timeout - */ - // NOTE: do not remove this comment, keeping as an example - // await waitForStateUpdate() - // { - // const state = pullLastResultOrThrow(); - // expect(state.status).equal("select-provider"); - // if (state.status !== "select-provider") return; - // expect(state.name.value).eq("") - // expect(state.url.value).eq("") - // } - - await assertNoPendingUpdate(); + expect(hookBehavior).deep.equal({ result: "ok" }); expect(handler.getCallingQueueState()).eq("empty"); }); }); diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx index b633a595f..c67c288dc 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx @@ -17,30 +17,17 @@ import { Amounts } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { Checkbox } from "../../components/Checkbox.js"; -import { LoadingError } from "../../components/LoadingError.js"; import { LightText, SmallLightText, SubTitle, - TermsOfService, Title, } from "../../components/styled/index.js"; -import { useTranslationContext } from "../../context/translation.js"; +import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Button } from "../../mui/Button.js"; import { TextField } from "../../mui/TextField.js"; import { State } from "./index.js"; -export function LoadingUriView({ error }: State.LoadingUriError): VNode { - const { i18n } = useTranslationContext(); - - return ( - <LoadingError - title={<i18n.Translate>Could not load</i18n.Translate>} - error={error} - /> - ); -} - export function ConfirmProviderView({ url, provider, @@ -88,9 +75,8 @@ export function ConfirmProviderView({ of service </i18n.Translate> </p> - {/* replace with <TermsOfService /> */} <Checkbox - label={<i18n.Translate>Accept terms of service</i18n.Translate>} + label={i18n.str`Accept terms of service`} name="terms" onToggle={tos.button.onClick} enabled={tos.value} |