This commit is contained in:
Sebastian 2022-12-15 17:12:03 -03:00
parent f93bd51499
commit f1f8f818db
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
45 changed files with 997 additions and 800 deletions

View File

@ -25,7 +25,7 @@ import {
LoadingUriView, LoadingUriView,
ShowButtonsAcceptedTosView, ShowButtonsAcceptedTosView,
ShowButtonsNonAcceptedTosView, ShowButtonsNonAcceptedTosView,
ShowTosContentView ShowTosContentView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -21,10 +21,8 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
import { buildTermsOfServiceState } from "./utils.js"; import { buildTermsOfServiceState } from "./utils.js";
export function useComponentState( export function useComponentState({ exchangeUrl, onChange }: Props): State {
{ exchangeUrl, onChange }: Props, const api = useBackendContext();
): State {
const api = useBackendContext()
const readOnly = !onChange; const readOnly = !onChange;
const [showContent, setShowContent] = useState<boolean>(readOnly); const [showContent, setShowContent] = useState<boolean>(readOnly);
const [errorAccepting, setErrorAccepting] = useState<Error | undefined>( const [errorAccepting, setErrorAccepting] = useState<Error | undefined>(

View File

@ -23,7 +23,7 @@ import { ComponentChildren, createContext, h, VNode } from "preact";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
import { wxApi, WxApiType } from "../wxApi.js"; import { wxApi, WxApiType } from "../wxApi.js";
type Type = WxApiType type Type = WxApiType;
const initial = wxApi; const initial = wxApi;
@ -31,7 +31,7 @@ const Context = createContext<Type>(initial);
type Props = Partial<WxApiType> & { type Props = Partial<WxApiType> & {
children: ComponentChildren; children: ComponentChildren;
} };
export const BackendProvider = ({ export const BackendProvider = ({
wallet, wallet,
@ -39,12 +39,11 @@ export const BackendProvider = ({
listener, listener,
children, children,
}: Props): VNode => { }: Props): VNode => {
return h(Context.Provider, { return h(Context.Provider, {
value: { value: {
wallet: wallet ?? initial.wallet, wallet: wallet ?? initial.wallet,
background: background ?? initial.background, background: background ?? initial.background,
listener: listener ?? initial.listener listener: listener ?? initial.listener,
}, },
children, children,
}); });

View File

@ -20,10 +20,13 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerDepositUri, amountStr, cancel, onSuccess }: Props, talerDepositUri,
): State { amountStr,
const api = useBackendContext() cancel,
onSuccess,
}: Props): State {
const api = useBackendContext();
const info = useAsyncAsHook(async () => { const info = useAsyncAsHook(async () => {
if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT"); if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT"); if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT");

View File

@ -42,7 +42,10 @@ describe("Deposit CTA states", () => {
}, },
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -54,9 +57,11 @@ describe("Deposit CTA states", () => {
if (error.operational) expect.fail(); if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT"); expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -83,7 +88,10 @@ describe("Deposit CTA states", () => {
}, },
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -95,9 +103,11 @@ describe("Deposit CTA states", () => {
expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2")); expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1")); expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -29,11 +29,13 @@ import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
import { RecursiveState } from "../../utils/index.js"; import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ amount: amountStr, onClose, onSuccess }: Props, amount: amountStr,
): RecursiveState<State> { onClose,
onSuccess,
}: Props): RecursiveState<State> {
const amount = Amounts.parseOrThrow(amountStr); const amount = Amounts.parseOrThrow(amountStr);
const api = useBackendContext() const api = useBackendContext();
const hook = useAsyncAsHook(() => const hook = useAsyncAsHook(() =>
api.wallet.call(WalletApiOperation.ListExchanges, {}), api.wallet.call(WalletApiOperation.ListExchanges, {}),

View File

@ -18,7 +18,7 @@ import {
AbsoluteTime, AbsoluteTime,
AmountJson, AmountJson,
PreparePayResult, PreparePayResult,
TalerErrorDetail TalerErrorDetail,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js"; import { HookError } from "../../hooks/useAsyncAsHook.js";

View File

@ -29,10 +29,13 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props, talerPayPullUri,
): State { onClose,
const api = useBackendContext() goToWalletManualWithdraw,
onSuccess,
}: Props): State {
const api = useBackendContext();
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, { const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, {
talerUri: talerPayPullUri, talerUri: talerPayPullUri,

View File

@ -19,7 +19,7 @@ import {
PreparePayResult, PreparePayResult,
PreparePayResultAlreadyConfirmed, PreparePayResultAlreadyConfirmed,
PreparePayResultInsufficientBalance, PreparePayResultInsufficientBalance,
PreparePayResultPaymentPossible PreparePayResultPaymentPossible,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js"; import { HookError } from "../../hooks/useAsyncAsHook.js";

View File

@ -28,11 +28,14 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { ButtonHandler } from "../../mui/handlers.js"; import { ButtonHandler } from "../../mui/handlers.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props, talerPayUri,
): State { cancel,
goToWalletManualWithdraw,
onSuccess,
}: Props): State {
const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined); const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined);
const api = useBackendContext() const api = useBackendContext();
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT"); if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");

View File

@ -45,7 +45,10 @@ describe("Payment CTA states", () => {
onSuccess: nullFunction, onSuccess: nullFunction,
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -56,11 +59,12 @@ describe("Payment CTA states", () => {
expect(error.hasError).true; expect(error.hasError).true;
expect(error.operational).false; expect(error.operational).false;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
it("should response with no balance", async () => { it("should response with no balance", async () => {
@ -86,7 +90,10 @@ describe("Payment CTA states", () => {
{ balances: [] }, { balances: [] },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -99,9 +106,11 @@ describe("Payment CTA states", () => {
expect(state.balance).undefined; expect(state.balance).undefined;
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -138,7 +147,10 @@ describe("Payment CTA states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -148,9 +160,11 @@ describe("Payment CTA states", () => {
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5")); expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -187,7 +201,10 @@ describe("Payment CTA states", () => {
], ],
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -201,11 +218,12 @@ describe("Payment CTA states", () => {
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.payHandler.onClick).not.undefined; expect(state.payHandler.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
it("should be able to pay (with fee)", async () => { it("should be able to pay (with fee)", async () => {
@ -241,7 +259,10 @@ describe("Payment CTA states", () => {
], ],
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -252,9 +273,11 @@ describe("Payment CTA states", () => {
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
expect(state.payHandler.onClick).not.undefined; expect(state.payHandler.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -297,7 +320,10 @@ describe("Payment CTA states", () => {
contractTerms: {}, contractTerms: {},
} as ConfirmPayResult); } as ConfirmPayResult);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -312,11 +338,12 @@ describe("Payment CTA states", () => {
if (state.payHandler.onClick === undefined) expect.fail(); if (state.payHandler.onClick === undefined) expect.fail();
state.payHandler.onClick(); state.payHandler.onClick();
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
it("should not stay in ready state after pay with error", async () => { it("should not stay in ready state after pay with error", async () => {
@ -357,7 +384,10 @@ describe("Payment CTA states", () => {
lastError: { code: 1 }, lastError: { code: 1 },
} as ConfirmPayResult); } as ConfirmPayResult);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -386,11 +416,12 @@ describe("Payment CTA states", () => {
lastError: { code: 1 }, lastError: { code: 1 },
}); });
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
it("should update balance if a coins is withdraw", async () => { it("should update balance if a coins is withdraw", async () => {
@ -455,13 +486,16 @@ describe("Payment CTA states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
}, },
(state) => { (state) => {
if (state.status !== "ready") expect.fail() if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10")); expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1")); // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
@ -470,15 +504,17 @@ describe("Payment CTA states", () => {
handler.notifyEventFromWallet(NotificationType.CoinWithdrawn); handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
}, },
(state) => { (state) => {
if (state.status !== "ready") expect.fail() if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15")); expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9")); expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1")); // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).not.undefined; expect(state.payHandler.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -19,10 +19,12 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useBackendContext } from "../../context/backend.js"; import { useBackendContext } from "../../context/backend.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerRecoveryUri, onCancel, onSuccess }: Props, talerRecoveryUri,
): State { onCancel,
const api = useBackendContext() onSuccess,
}: Props): State {
const api = useBackendContext();
if (!talerRecoveryUri) { if (!talerRecoveryUri) {
return { return {
status: "loading-uri", status: "loading-uri",

View File

@ -24,7 +24,7 @@ import {
IgnoredView, IgnoredView,
InProgressView, InProgressView,
LoadingUriView, LoadingUriView,
ReadyView ReadyView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -21,10 +21,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerRefundUri, cancel, onSuccess }: Props, talerRefundUri,
): State { cancel,
const api = useBackendContext() onSuccess,
}: Props): State {
const api = useBackendContext();
const [ignored, setIgnored] = useState(false); const [ignored, setIgnored] = useState(false);
const info = useAsyncAsHook(async () => { const info = useAsyncAsHook(async () => {

View File

@ -22,12 +22,16 @@
import { import {
Amounts, Amounts,
NotificationType, NotificationType,
OrderShortInfo OrderShortInfo,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai"; import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js"; import { tests } from "../../../../web-util/src/index.browser.js";
import { createWalletApiMock, mountHook, nullFunction } from "../../test-utils.js"; import {
createWalletApiMock,
mountHook,
nullFunction,
} from "../../test-utils.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
describe("Refund CTA states", () => { describe("Refund CTA states", () => {
@ -38,9 +42,12 @@ describe("Refund CTA states", () => {
talerRefundUri: undefined, talerRefundUri: undefined,
cancel: nullFunction, cancel: nullFunction,
onSuccess: nullFunction, onSuccess: nullFunction,
} };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -52,9 +59,11 @@ describe("Refund CTA states", () => {
if (error.operational) expect.fail(); if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-REFUND"); expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -83,7 +92,10 @@ describe("Refund CTA states", () => {
} as OrderShortInfo, } as OrderShortInfo,
}); });
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -97,9 +109,11 @@ describe("Refund CTA states", () => {
expect(state.orderId).eq("orderId1"); expect(state.orderId).eq("orderId1");
expect(state.products).undefined; expect(state.products).undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -132,14 +146,17 @@ describe("Refund CTA states", () => {
} as OrderShortInfo, } as OrderShortInfo,
}); });
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
}, },
(state) => { (state) => {
if (state.status !== "ready") expect.fail() if (state.status !== "ready") expect.fail();
if (state.error) expect.fail() if (state.error) expect.fail();
expect(state.accept.onClick).not.undefined; expect(state.accept.onClick).not.undefined;
expect(state.merchantName).eq("the merchant name"); expect(state.merchantName).eq("the merchant name");
expect(state.orderId).eq("orderId1"); expect(state.orderId).eq("orderId1");
@ -149,13 +166,15 @@ describe("Refund CTA states", () => {
state.ignore.onClick(); state.ignore.onClick();
}, },
(state) => { (state) => {
if (state.status !== "ignored") expect.fail() if (state.status !== "ignored") expect.fail();
if (state.error) expect.fail() if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name"); expect(state.merchantName).eq("the merchant name");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -220,13 +239,16 @@ describe("Refund CTA states", () => {
} as OrderShortInfo, } as OrderShortInfo,
}); });
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
}, },
(state) => { (state) => {
if (state.status !== "in-progress") expect.fail() if (state.status !== "in-progress") expect.fail();
if (state.error) expect.fail(); if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name"); expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined; expect(state.products).undefined;
@ -236,7 +258,7 @@ describe("Refund CTA states", () => {
handler.notifyEventFromWallet(NotificationType.RefreshMelted); handler.notifyEventFromWallet(NotificationType.RefreshMelted);
}, },
(state) => { (state) => {
if (state.status !== "in-progress") expect.fail() if (state.status !== "in-progress") expect.fail();
if (state.error) expect.fail(); if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name"); expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined; expect(state.products).undefined;
@ -246,16 +268,17 @@ describe("Refund CTA states", () => {
handler.notifyEventFromWallet(NotificationType.RefreshMelted); handler.notifyEventFromWallet(NotificationType.RefreshMelted);
}, },
(state) => { (state) => {
if (state.status !== "ready") expect.fail() if (state.status !== "ready") expect.fail();
if (state.error) expect.fail(); if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name"); expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined; expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2")); expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -24,7 +24,7 @@ import {
AcceptedView, AcceptedView,
IgnoredView, IgnoredView,
LoadingUriView, LoadingUriView,
ReadyView ReadyView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -20,10 +20,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerTipUri, onCancel, onSuccess }: Props, talerTipUri,
): State { onCancel,
const api = useBackendContext() onSuccess,
}: Props): State {
const api = useBackendContext();
const tipInfo = useAsyncAsHook(async () => { const tipInfo = useAsyncAsHook(async () => {
if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP"); if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
const tip = await api.wallet.call(WalletApiOperation.PrepareTip, { const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {

View File

@ -36,9 +36,12 @@ describe("Tip CTA states", () => {
talerTipUri: undefined, talerTipUri: undefined,
onCancel: nullFunction, onCancel: nullFunction,
onSuccess: nullFunction, onSuccess: nullFunction,
} };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -50,9 +53,11 @@ describe("Tip CTA states", () => {
if (error.operational) expect.fail(); if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-TIP"); expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -75,9 +80,12 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd", talerTipUri: "taler://tip/asd",
onCancel: nullFunction, onCancel: nullFunction,
onSuccess: nullFunction, onSuccess: nullFunction,
} };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -96,7 +104,10 @@ describe("Tip CTA states", () => {
handler.addWalletCallResponse(WalletApiOperation.AcceptTip); handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
state.accept.onClick(); state.accept.onClick();
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, { handler.addWalletCallResponse(
WalletApiOperation.PrepareTip,
undefined,
{
accepted: true, accepted: true,
exchangeBaseUrl: "exchange url", exchangeBaseUrl: "exchange url",
merchantBaseUrl: "merchant url", merchantBaseUrl: "merchant url",
@ -106,19 +117,21 @@ describe("Tip CTA states", () => {
t_s: 1, t_s: 1,
}, },
tipAmountRaw: "", tipAmountRaw: "",
}); },
);
}, },
(state) => { (state) => {
if (state.status !== "accepted") expect.fail() if (state.status !== "accepted") expect.fail();
if (state.error) expect.fail(); if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1")); expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url"); expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url"); expect(state.exchangeBaseUrl).eq("exchange url");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -140,9 +153,12 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd", talerTipUri: "taler://tip/asd",
onCancel: nullFunction, onCancel: nullFunction,
onSuccess: nullFunction, onSuccess: nullFunction,
} };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -156,9 +172,11 @@ describe("Tip CTA states", () => {
//FIXME: add ignore button //FIXME: add ignore button
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -181,9 +199,12 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd", talerTipUri: "taler://tip/asd",
onCancel: nullFunction, onCancel: nullFunction,
onSuccess: nullFunction, onSuccess: nullFunction,
} };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => { ({ status, error }) => {
expect(status).equals("loading"); expect(status).equals("loading");
expect(error).undefined; expect(error).undefined;
@ -195,10 +216,11 @@ describe("Tip CTA states", () => {
expect(state.merchantBaseUrl).eq("merchant url"); expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url"); expect(state.exchangeBaseUrl).eq("exchange url");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -17,7 +17,7 @@
import { import {
Amounts, Amounts,
TalerErrorDetail, TalerErrorDetail,
TalerProtocolTimestamp TalerProtocolTimestamp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { isFuture, parse } from "date-fns"; import { isFuture, parse } from "date-fns";
@ -26,10 +26,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ amount: amountStr, onClose, onSuccess }: Props, amount: amountStr,
): State { onClose,
const api = useBackendContext() onSuccess,
}: Props): State {
const api = useBackendContext();
const amount = Amounts.parseOrThrow(amountStr); const amount = Amounts.parseOrThrow(amountStr);
const [subject, setSubject] = useState<string | undefined>(); const [subject, setSubject] = useState<string | undefined>();

View File

@ -17,7 +17,7 @@
import { import {
AbsoluteTime, AbsoluteTime,
AmountJson, AmountJson,
TalerErrorDetail TalerErrorDetail,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js"; import { HookError } from "../../hooks/useAsyncAsHook.js";

View File

@ -18,7 +18,7 @@ import {
AbsoluteTime, AbsoluteTime,
Amounts, Amounts,
TalerErrorDetail, TalerErrorDetail,
TalerProtocolTimestamp TalerProtocolTimestamp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -26,10 +26,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ talerPayPushUri, onClose, onSuccess }: Props, talerPayPushUri,
): State { onClose,
const api = useBackendContext() onSuccess,
}: Props): State {
const api = useBackendContext();
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, { return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
talerUri: talerPayPushUri, talerUri: talerPayPushUri,

View File

@ -22,7 +22,7 @@ import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js"; import { compose, StateViewMap } from "../../utils/index.js";
import { import {
useComponentStateFromParams, useComponentStateFromParams,
useComponentStateFromURI useComponentStateFromURI,
} from "./state.js"; } from "./state.js";
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js"; import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";

View File

@ -19,7 +19,7 @@ import {
AmountJson, AmountJson,
Amounts, Amounts,
ExchangeListItem, ExchangeListItem,
ExchangeTosStatus ExchangeTosStatus,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -29,10 +29,12 @@ import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
import { RecursiveState } from "../../utils/index.js"; import { RecursiveState } from "../../utils/index.js";
import { PropsFromParams, PropsFromURI, State } from "./index.js"; import { PropsFromParams, PropsFromURI, State } from "./index.js";
export function useComponentStateFromParams( export function useComponentStateFromParams({
{ amount, cancel, onSuccess }: PropsFromParams, amount,
): RecursiveState<State> { cancel,
const api = useBackendContext() onSuccess,
}: PropsFromParams): RecursiveState<State> {
const api = useBackendContext();
const uriInfoHook = useAsyncAsHook(async () => { const uriInfoHook = useAsyncAsHook(async () => {
const exchanges = await api.wallet.call( const exchanges = await api.wallet.call(
WalletApiOperation.ListExchanges, WalletApiOperation.ListExchanges,
@ -87,10 +89,12 @@ export function useComponentStateFromParams(
); );
} }
export function useComponentStateFromURI( export function useComponentStateFromURI({
{ talerWithdrawUri, cancel, onSuccess }: PropsFromURI, talerWithdrawUri,
): RecursiveState<State> { cancel,
const api = useBackendContext() onSuccess,
}: PropsFromURI): RecursiveState<State> {
const api = useBackendContext();
/** /**
* Ask the wallet about the withdraw URI * Ask the wallet about the withdraw URI
*/ */
@ -175,7 +179,7 @@ function exchangeSelectionState(
exchangeList: ExchangeListItem[], exchangeList: ExchangeListItem[],
defaultExchange: string | undefined, defaultExchange: string | undefined,
): RecursiveState<State> { ): RecursiveState<State> {
const api = useBackendContext() const api = useBackendContext();
const selectedExchange = useSelectedExchange({ const selectedExchange = useSelectedExchange({
currency: chosenAmount.currency, currency: chosenAmount.currency,
defaultExchange, defaultExchange,

View File

@ -64,7 +64,7 @@ const exchanges: ExchangeListItem[] = [
const nullFunction = async (): Promise<void> => { const nullFunction = async (): Promise<void> => {
null; null;
} };
describe("Withdraw CTA states", () => { describe("Withdraw CTA states", () => {
it("should tell the user that the URI is missing", async () => { it("should tell the user that the URI is missing", async () => {
@ -76,7 +76,10 @@ describe("Withdraw CTA states", () => {
onSuccess: nullFunction, onSuccess: nullFunction,
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -87,9 +90,11 @@ describe("Withdraw CTA states", () => {
if (error.operational) expect.fail(); if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL"); expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -110,7 +115,10 @@ describe("Withdraw CTA states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -118,9 +126,11 @@ describe("Withdraw CTA states", () => {
expect(status).equals("no-exchange"); expect(status).equals("no-exchange");
expect(error).undefined; expect(error).undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -153,7 +163,10 @@ describe("Withdraw CTA states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -171,9 +184,11 @@ describe("Withdraw CTA states", () => {
expect(state.doWithdrawal.onClick).not.undefined; expect(state.doWithdrawal.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -221,7 +236,10 @@ describe("Withdraw CTA states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => { ({ status }) => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
@ -251,9 +269,11 @@ describe("Withdraw CTA states", () => {
expect(state.doWithdrawal.onClick).not.undefined; expect(state.doWithdrawal.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -25,9 +25,11 @@ export function useAutoOpenPermissions(): ToggleHandler {
const [enabled, setEnabled] = useState(false); const [enabled, setEnabled] = useState(false);
const [error, setError] = useState<TalerError | undefined>(); const [error, setError] = useState<TalerError | undefined>();
const toggle = async (): Promise<void> => { const toggle = async (): Promise<void> => {
return handleAutoOpenPerm(enabled, setEnabled, api.background).catch((e) => { return handleAutoOpenPerm(enabled, setEnabled, api.background).catch(
(e) => {
setError(TalerError.fromException(e)); setError(TalerError.fromException(e));
}); },
);
}; };
useEffect(() => { useEffect(() => {

View File

@ -28,7 +28,7 @@ export function useBackupDeviceName(): BackupDeviceName {
name: "", name: "",
update: () => Promise.resolve(), update: () => Promise.resolve(),
}); });
const api = useBackendContext() const api = useBackendContext();
useEffect(() => { useEffect(() => {
async function run(): Promise<void> { async function run(): Promise<void> {

View File

@ -23,12 +23,14 @@ import { platform } from "../platform/api.js";
export function useClipboardPermissions(): ToggleHandler { export function useClipboardPermissions(): ToggleHandler {
const [enabled, setEnabled] = useState(false); const [enabled, setEnabled] = useState(false);
const [error, setError] = useState<TalerError | undefined>(); const [error, setError] = useState<TalerError | undefined>();
const api = useBackendContext() const api = useBackendContext();
const toggle = async (): Promise<void> => { const toggle = async (): Promise<void> => {
return handleClipboardPerm(enabled, setEnabled, api.background).catch((e) => { return handleClipboardPerm(enabled, setEnabled, api.background).catch(
(e) => {
setError(TalerError.fromException(e)); setError(TalerError.fromException(e));
}); },
);
}; };
useEffect(() => { useEffect(() => {

View File

@ -444,8 +444,8 @@ function registerTalerHeaderListener(
info: chrome.tabs.TabChangeInfo, info: chrome.tabs.TabChangeInfo,
): Promise<void> { ): Promise<void> {
if (tabId < 0) return; if (tabId < 0) return;
const tabLocationHasBeenUpdated = info.status === "complete" const tabLocationHasBeenUpdated = info.status === "complete";
const tabTitleHasBeenUpdated = info.title !== undefined const tabTitleHasBeenUpdated = info.title !== undefined;
if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) { if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) {
const uri = await findTalerUriInTab(tabId); const uri = await findTalerUriInTab(tabId);
if (!uri) return; if (!uri) return;

View File

@ -34,18 +34,21 @@ setupI18n("en", { en: {} });
setupPlatform(chromeAPI); setupPlatform(chromeAPI);
describe("All the examples:", () => { describe("All the examples:", () => {
const cms = parseGroupImport({ popup, wallet, cta, mui, components }) const cms = parseGroupImport({ popup, wallet, cta, mui, components });
cms.forEach(group => { cms.forEach((group) => {
describe(`Example for group "${group.title}:"`, () => { describe(`Example for group "${group.title}:"`, () => {
group.list.forEach(component => { group.list.forEach((component) => {
describe(`Component ${component.name}:`, () => { describe(`Component ${component.name}:`, () => {
component.examples.forEach(example => { component.examples.forEach((example) => {
it(`should render example: ${example.name}`, () => { it(`should render example: ${example.name}`, () => {
renderNodeOrBrowser(example.render.component, example.render.props) renderNodeOrBrowser(
}) example.render.component,
}) example.render.props,
}) );
}) });
}) });
}) });
});
});
});
}); });

View File

@ -54,7 +54,7 @@ export function createExample<Props>(
return { return {
component: Render, component: Render,
props: evaluatedProps props: evaluatedProps,
}; };
} }
@ -74,7 +74,7 @@ export function createExampleWithCustomContext<Props, ContextProps>(
return { return {
component: WithContext, component: WithContext,
props: evaluatedProps props: evaluatedProps,
}; };
} }
@ -253,7 +253,7 @@ type Subscriptions = {
export function createWalletApiMock(): { export function createWalletApiMock(): {
handler: MockHandler; handler: MockHandler;
TestingContext: FunctionalComponent<{ children: ComponentChildren }> TestingContext: FunctionalComponent<{ children: ComponentChildren }>;
} { } {
const calls = new Array<CallRecord>(); const calls = new Array<CallRecord>();
const subscriptions: Subscriptions = {}; const subscriptions: Subscriptions = {};
@ -358,13 +358,21 @@ export function createWalletApiMock(): {
}, },
}; };
function TestingContext({ children }: { children: ComponentChildren }): VNode { function TestingContext({
return create(BackendProvider, { children,
}: {
children: ComponentChildren;
}): VNode {
return create(
BackendProvider,
{
wallet: mock.wallet, wallet: mock.wallet,
background: mock.background, background: mock.background,
listener: mock.listener, listener: mock.listener,
children, children,
}, children) },
children,
);
} }
return { handler, TestingContext }; return { handler, TestingContext };

View File

@ -14,22 +14,21 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { import { TalerErrorDetail } from "@gnu-taler/taler-util";
TalerErrorDetail
} from "@gnu-taler/taler-util";
import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core"; import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js"; import { HookError } from "../../hooks/useAsyncAsHook.js";
import { import {
ButtonHandler, ButtonHandler,
TextFieldHandler, TextFieldHandler,
ToggleHandler ToggleHandler,
} from "../../mui/handlers.js"; } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js"; import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
import { import {
ConfirmProviderView, LoadingUriView, ConfirmProviderView,
SelectProviderView LoadingUriView,
SelectProviderView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -17,11 +17,11 @@
import { import {
canonicalizeBaseUrl, canonicalizeBaseUrl,
Codec, Codec,
TalerErrorDetail TalerErrorDetail,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
codecForSyncTermsOfServiceResponse, codecForSyncTermsOfServiceResponse,
WalletApiOperation WalletApiOperation,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js"; import { useBackendContext } from "../../context/backend.js";
@ -143,10 +143,13 @@ function useUrlState<T>(
return state; return state;
} }
export function useComponentState( export function useComponentState({
{ currency, onBack, onComplete, onPaymentRequired }: Props, currency,
): State { onBack,
const api = useBackendContext() onComplete,
onPaymentRequired,
}: Props): State {
const api = useBackendContext();
const [url, setHost] = useState<string | undefined>(); const [url, setHost] = useState<string | undefined>();
const [name, setName] = useState<string | undefined>(); const [name, setName] = useState<string | undefined>();
const [tos, setTos] = useState(false); const [tos, setTos] = useState(false);

View File

@ -21,9 +21,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js"; import { tests } from "../../../../web-util/src/index.browser.js";
import { import { createWalletApiMock, nullFunction } from "../../test-utils.js";
createWalletApiMock, nullFunction
} from "../../test-utils.js";
import { Props } from "./index.js"; import { Props } from "./index.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
@ -34,21 +32,24 @@ const props: Props = {
onPaymentRequired: nullFunction, onPaymentRequired: nullFunction,
}; };
describe("AddBackupProvider states", () => { describe("AddBackupProvider states", () => {
it("should start in 'select-provider' state", async () => { it("should start in 'select-provider' state", async () => {
const { handler, TestingContext } = createWalletApiMock(); const { handler, TestingContext } = createWalletApiMock();
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
(state) => { (state) => {
expect(state.status).equal("select-provider"); expect(state.status).equal("select-provider");
if (state.status !== "select-provider") return; if (state.status !== "select-provider") return;
expect(state.name.value).eq(""); expect(state.name.value).eq("");
expect(state.url.value).eq(""); expect(state.url.value).eq("");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -20,7 +20,7 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
import { import {
AmountFieldHandler, AmountFieldHandler,
ButtonHandler, ButtonHandler,
SelectFieldHandler SelectFieldHandler,
} from "../../mui/handlers.js"; } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js"; import { compose, StateViewMap } from "../../utils/index.js";
import { ManageAccountPage } from "../ManageAccount/index.js"; import { ManageAccountPage } from "../ManageAccount/index.js";
@ -30,7 +30,7 @@ import {
LoadingErrorView, LoadingErrorView,
NoAccountToDepositView, NoAccountToDepositView,
NoEnoughBalanceView, NoEnoughBalanceView,
ReadyView ReadyView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -21,7 +21,7 @@ import {
KnownBankAccountsInfo, KnownBankAccountsInfo,
parsePaytoUri, parsePaytoUri,
PaytoUri, PaytoUri,
stringifyPaytoUri stringifyPaytoUri,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -29,10 +29,13 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props, amount: amountStr,
): State { currency: currencyStr,
const api = useBackendContext() onCancel,
onSuccess,
}: Props): State {
const api = useBackendContext();
const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr); const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
const currency = parsed !== undefined ? parsed.currency : currencyStr; const currency = parsed !== undefined ? parsed.currency : currencyStr;
@ -162,7 +165,11 @@ export function useComponentState(
async function updateAmount(newAmount: AmountJson): Promise<void> { async function updateAmount(newAmount: AmountJson): Promise<void> {
// const parsed = Amounts.parse(`${currency}:${numStr}`); // const parsed = Amounts.parse(`${currency}:${numStr}`);
try { try {
const result = await getFeeForAmount(currentAccount, newAmount, api.wallet); const result = await getFeeForAmount(
currentAccount,
newAmount,
api.wallet,
);
setAmount(newAmount); setAmount(newAmount);
setFee(result); setFee(result);
} catch (e) { } catch (e) {

View File

@ -23,14 +23,12 @@ import {
Amounts, Amounts,
DepositGroupFees, DepositGroupFees,
parsePaytoUri, parsePaytoUri,
stringifyPaytoUri stringifyPaytoUri,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai"; import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js"; import { tests } from "../../../../web-util/src/index.browser.js";
import { import { createWalletApiMock, nullFunction } from "../../test-utils.js";
createWalletApiMock, nullFunction
} from "../../test-utils.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
@ -71,16 +69,21 @@ describe("DepositPage states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
({ status }) => { ({ status }) => {
expect(status).equal("no-enough-balance"); expect(status).equal("no-enough-balance");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -107,16 +110,21 @@ describe("DepositPage states", () => {
}, },
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
({ status }) => { ({ status }) => {
expect(status).equal("no-accounts"); expect(status).equal("no-accounts");
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -161,7 +169,10 @@ describe("DepositPage states", () => {
withoutFee(), withoutFee(),
); );
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
@ -176,9 +187,11 @@ describe("DepositPage states", () => {
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0")); expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined; expect(state.depositHandler.onClick).undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -218,7 +231,10 @@ describe("DepositPage states", () => {
const accountSelected = stringifyPaytoUri(ibanPayto.uri); const accountSelected = stringifyPaytoUri(ibanPayto.uri);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
@ -246,9 +262,11 @@ describe("DepositPage states", () => {
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)); expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.depositHandler.onClick).undefined; expect(state.depositHandler.onClick).undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
@ -292,7 +310,10 @@ describe("DepositPage states", () => {
const accountSelected = stringifyPaytoUri(ibanPayto.uri); const accountSelected = stringifyPaytoUri(ibanPayto.uri);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
@ -331,13 +352,16 @@ describe("DepositPage states", () => {
expect(state.account.value).eq(accountSelected); expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10")); expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`)); expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
expect(state.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`)); expect(state.totalToDeposit).deep.eq(
Amounts.parseOrThrow(`${currency}:7`),
);
expect(state.depositHandler.onClick).not.undefined; expect(state.depositHandler.onClick).not.undefined;
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -22,10 +22,8 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { assertUnreachable, RecursiveState } from "../../utils/index.js"; import { assertUnreachable, RecursiveState } from "../../utils/index.js";
import { Contact, Props, State } from "./index.js"; import { Contact, Props, State } from "./index.js";
export function useComponentState( export function useComponentState(props: Props): RecursiveState<State> {
props: Props, const api = useBackendContext();
): RecursiveState<State> {
const api = useBackendContext()
const parsedInitialAmount = !props.amount const parsedInitialAmount = !props.amount
? undefined ? undefined
: Amounts.parse(props.amount); : Amounts.parse(props.amount);
@ -43,17 +41,17 @@ export function useComponentState(
: [ : [
{ {
name: "International Bank", name: "International Bank",
icon_type: 'bank', icon_type: "bank",
description: "account ending with 3454", description: "account ending with 3454",
}, },
{ {
name: "Max", name: "Max",
icon_type: 'bank', icon_type: "bank",
description: "account ending with 3454", description: "account ending with 3454",
}, },
{ {
name: "Alex", name: "Alex",
icon_type: 'bank', icon_type: "bank",
description: "account ending with 3454", description: "account ending with 3454",
}, },
]; ];

View File

@ -23,7 +23,7 @@ import {
Amounts, Amounts,
ExchangeEntryStatus, ExchangeEntryStatus,
ExchangeListItem, ExchangeListItem,
ExchangeTosStatus ExchangeTosStatus,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai"; import { expect } from "chai";
@ -59,7 +59,10 @@ describe("Destination selection states", () => {
goToWalletWalletInvoice: nullFunction, goToWalletWalletInvoice: nullFunction,
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => { ({ status }) => {
expect(status).equal("loading"); expect(status).equal("loading");
}, },
@ -79,13 +82,16 @@ describe("Destination selection states", () => {
expect(state.goToBank.onClick).eq(undefined); expect(state.goToBank.onClick).eq(undefined);
expect(state.goToWallet.onClick).eq(undefined); expect(state.goToWallet.onClick).eq(undefined);
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0")); expect(state.amountHandler.value).deep.eq(
Amounts.parseOrThrow("ARS:0"),
);
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
it("should be possible to start with an amount specified in request params", async () => { it("should be possible to start with an amount specified in request params", async () => {
@ -98,7 +104,10 @@ describe("Destination selection states", () => {
amount: "ARS:2", amount: "ARS:2",
}; };
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [ const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
// ({ status }) => { // ({ status }) => {
// expect(status).equal("loading"); // expect(status).equal("loading");
// }, // },
@ -108,12 +117,15 @@ describe("Destination selection states", () => {
expect(state.goToBank.onClick).not.eq(undefined); expect(state.goToBank.onClick).not.eq(undefined);
expect(state.goToWallet.onClick).not.eq(undefined); expect(state.goToWallet.onClick).not.eq(undefined);
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:2")); expect(state.amountHandler.value).deep.eq(
Amounts.parseOrThrow("ARS:2"),
);
}, },
], TestingContext) ],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" }) expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty"); expect(handler.getCallingQueueState()).eq("empty");
}); });
}); });

View File

@ -18,7 +18,7 @@ import {
DenomOperationMap, DenomOperationMap,
ExchangeFullDetails, ExchangeFullDetails,
ExchangeListItem, ExchangeListItem,
FeeDescriptionPair FeeDescriptionPair,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js"; import { Loading } from "../../components/Loading.js";
import { HookError } from "../../hooks/useAsyncAsHook.js"; import { HookError } from "../../hooks/useAsyncAsHook.js";
@ -32,7 +32,7 @@ import {
NoExchangesView, NoExchangesView,
PrivacyContentView, PrivacyContentView,
ReadyView, ReadyView,
TosContentView TosContentView,
} from "./views.js"; } from "./views.js";
export interface Props { export interface Props {

View File

@ -17,17 +17,20 @@
import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util"; import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util";
import { import {
createPairTimeline, createPairTimeline,
WalletApiOperation WalletApiOperation,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js"; import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ onCancel, onSelection, list: exchanges, currentExchange }: Props, onCancel,
): State { onSelection,
const api = useBackendContext() list: exchanges,
currentExchange,
}: Props): State {
const api = useBackendContext();
const initialValue = exchanges.findIndex( const initialValue = exchanges.findIndex(
(e) => e.exchangeBaseUrl === currentExchange, (e) => e.exchangeBaseUrl === currentExchange,
); );

View File

@ -20,7 +20,7 @@ import { HookError } from "../../hooks/useAsyncAsHook.js";
import { import {
ButtonHandler, ButtonHandler,
SelectFieldHandler, SelectFieldHandler,
TextFieldHandler TextFieldHandler,
} from "../../mui/handlers.js"; } from "../../mui/handlers.js";
import { compose, StateViewMap } from "../../utils/index.js"; import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";

View File

@ -17,7 +17,7 @@
import { import {
KnownBankAccountsInfo, KnownBankAccountsInfo,
parsePaytoUri, parsePaytoUri,
stringifyPaytoUri stringifyPaytoUri,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -25,10 +25,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { AccountByType, Props, State } from "./index.js"; import { AccountByType, Props, State } from "./index.js";
export function useComponentState( export function useComponentState({
{ currency, onAccountAdded, onCancel }: Props, currency,
): State { onAccountAdded,
const api = useBackendContext() onCancel,
}: Props): State {
const api = useBackendContext();
const hook = useAsyncAsHook(() => const hook = useAsyncAsHook(() =>
api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }), api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
); );

View File

@ -21,7 +21,7 @@ import { compose, StateViewMap } from "../../utils/index.js";
import { useComponentState } from "./state.js"; import { useComponentState } from "./state.js";
import { LoadingUriView, ReadyView } from "./views.js"; import { LoadingUriView, ReadyView } from "./views.js";
export type Props = object export type Props = object;
export type State = State.Loading | State.LoadingUriError | State.Ready; export type State = State.Loading | State.LoadingUriError | State.Ready;

View File

@ -20,7 +20,7 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js"; import { Props, State } from "./index.js";
export function useComponentState(p: Props): State { export function useComponentState(p: Props): State {
const api = useBackendContext() const api = useBackendContext();
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
return await api.wallet.call( return await api.wallet.call(
WalletApiOperation.GetUserAttentionRequests, WalletApiOperation.GetUserAttentionRequests,

View File

@ -156,8 +156,8 @@ export type WxApiType = {
background: BackgroundApiClient; background: BackgroundApiClient;
listener: { listener: {
onUpdateNotification: typeof onUpdateNotification; onUpdateNotification: typeof onUpdateNotification;
} };
} };
export const wxApi = { export const wxApi = {
wallet: new WxWalletCoreApiClient(), wallet: new WxWalletCoreApiClient(),