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,
ShowButtonsAcceptedTosView,
ShowButtonsNonAcceptedTosView,
ShowTosContentView
ShowTosContentView,
} from "./views.js";
export interface Props {

View File

@ -21,10 +21,8 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js";
import { buildTermsOfServiceState } from "./utils.js";
export function useComponentState(
{ exchangeUrl, onChange }: Props,
): State {
const api = useBackendContext()
export function useComponentState({ exchangeUrl, onChange }: Props): State {
const api = useBackendContext();
const readOnly = !onChange;
const [showContent, setShowContent] = useState<boolean>(readOnly);
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 { wxApi, WxApiType } from "../wxApi.js";
type Type = WxApiType
type Type = WxApiType;
const initial = wxApi;
@ -31,7 +31,7 @@ const Context = createContext<Type>(initial);
type Props = Partial<WxApiType> & {
children: ComponentChildren;
}
};
export const BackendProvider = ({
wallet,
@ -39,12 +39,11 @@ export const BackendProvider = ({
listener,
children,
}: Props): VNode => {
return h(Context.Provider, {
value: {
wallet: wallet ?? initial.wallet,
background: background ?? initial.background,
listener: listener ?? initial.listener
listener: listener ?? initial.listener,
},
children,
});

View File

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

View File

@ -42,21 +42,26 @@ describe("Deposit CTA states", () => {
},
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading-uri");
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
},
], TestingContext)
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -83,21 +88,26 @@ describe("Deposit CTA states", () => {
},
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equals("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.confirm.onClick).not.undefined;
expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equals("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.confirm.onClick).not.undefined;
expect(state.cost).deep.eq(Amounts.parseOrThrow("EUR:1.2"));
expect(state.fee).deep.eq(Amounts.parseOrThrow("EUR:0.2"));
expect(state.effective).deep.eq(Amounts.parseOrThrow("EUR:1"));
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
});

View File

@ -29,11 +29,13 @@ import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";
export function useComponentState(
{ amount: amountStr, onClose, onSuccess }: Props,
): RecursiveState<State> {
export function useComponentState({
amount: amountStr,
onClose,
onSuccess,
}: Props): RecursiveState<State> {
const amount = Amounts.parseOrThrow(amountStr);
const api = useBackendContext()
const api = useBackendContext();
const hook = useAsyncAsHook(() =>
api.wallet.call(WalletApiOperation.ListExchanges, {}),
@ -158,8 +160,8 @@ export function useComponentState(
subject === undefined
? undefined
: !subject
? "Can't be empty"
: undefined,
? "Can't be empty"
: undefined,
value: subject ?? "",
onInput: async (e) => setSubject(e),
},

View File

@ -18,7 +18,7 @@ import {
AbsoluteTime,
AmountJson,
PreparePayResult,
TalerErrorDetail
TalerErrorDetail,
} from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.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 { Props, State } from "./index.js";
export function useComponentState(
{ talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
talerPayPullUri,
onClose,
goToWalletManualWithdraw,
onSuccess,
}: Props): State {
const api = useBackendContext();
const hook = useAsyncAsHook(async () => {
const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, {
talerUri: talerPayPullUri,

View File

@ -19,7 +19,7 @@ import {
PreparePayResult,
PreparePayResultAlreadyConfirmed,
PreparePayResultInsufficientBalance,
PreparePayResultPaymentPossible
PreparePayResultPaymentPossible,
} from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.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 { Props, State } from "./index.js";
export function useComponentState(
{ talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props,
): State {
export function useComponentState({
talerPayUri,
cancel,
goToWalletManualWithdraw,
onSuccess,
}: Props): State {
const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined);
const api = useBackendContext()
const api = useBackendContext();
const hook = useAsyncAsHook(async () => {
if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");

View File

@ -45,22 +45,26 @@ describe("Payment CTA states", () => {
onSuccess: nullFunction,
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (error === undefined) expect.fail();
expect(error.hasError).true;
expect(error.operational).false;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (error === undefined) expect.fail();
expect(error.hasError).true;
expect(error.operational).false;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
it("should response with no balance", async () => {
@ -86,22 +90,27 @@ describe("Payment CTA states", () => {
{ balances: [] },
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "no-balance-for-currency") {
expect(state).eq({});
return;
}
expect(state.balance).undefined;
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "no-balance-for-currency") {
expect(state).eq({});
return;
}
expect(state.balance).undefined;
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -138,19 +147,24 @@ describe("Payment CTA states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "no-enough-balance") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "no-enough-balance") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:5"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -187,25 +201,29 @@ describe("Payment CTA states", () => {
],
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({});
return;
}
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.payHandler.onClick).not.undefined;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({});
return;
}
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.payHandler.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
it("should be able to pay (with fee)", async () => {
@ -241,20 +259,25 @@ describe("Payment CTA states", () => {
],
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
expect(state.payHandler.onClick).not.undefined;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
expect(state.payHandler.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -297,26 +320,30 @@ describe("Payment CTA states", () => {
contractTerms: {},
} as ConfirmPayResult);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({});
return;
}
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
if (state.payHandler.onClick === undefined) expect.fail();
state.payHandler.onClick();
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({});
return;
}
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
if (state.payHandler.onClick === undefined) expect.fail();
state.payHandler.onClick();
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
it("should not stay in ready state after pay with error", async () => {
@ -357,40 +384,44 @@ describe("Payment CTA states", () => {
lastError: { code: 1 },
} as ConfirmPayResult);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
if (state.payHandler.onClick === undefined) expect.fail();
state.payHandler.onClick();
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).undefined;
if (state.payHandler.error === undefined) expect.fail();
//FIXME: error message here is bad
expect(state.payHandler.error.errorDetail.hint).eq(
"could not confirm payment",
);
expect(state.payHandler.error.errorDetail.payResult).deep.equal({
type: ConfirmPayResultType.Pending,
lastError: { code: 1 },
});
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
if (state.payHandler.onClick === undefined) expect.fail();
state.payHandler.onClick();
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).undefined;
if (state.payHandler.error === undefined) expect.fail();
//FIXME: error message here is bad
expect(state.payHandler.error.errorDetail.hint).eq(
"could not confirm payment",
);
expect(state.payHandler.error.errorDetail.payResult).deep.equal({
type: ConfirmPayResultType.Pending,
lastError: { code: 1 },
});
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
it("should update balance if a coins is withdraw", async () => {
@ -455,30 +486,35 @@ describe("Payment CTA states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail()
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).not.undefined;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:10"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).not.undefined;
handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
},
(state) => {
if (state.status !== "ready") expect.fail()
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).not.undefined;
},
], TestingContext)
handler.notifyEventFromWallet(NotificationType.CoinWithdrawn);
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
expect(state.payHandler.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
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 { Props, State } from "./index.js";
export function useComponentState(
{ talerRecoveryUri, onCancel, onSuccess }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
talerRecoveryUri,
onCancel,
onSuccess,
}: Props): State {
const api = useBackendContext();
if (!talerRecoveryUri) {
return {
status: "loading-uri",

View File

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

View File

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

View File

@ -22,12 +22,16 @@
import {
Amounts,
NotificationType,
OrderShortInfo
OrderShortInfo,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js";
import { createWalletApiMock, mountHook, nullFunction } from "../../test-utils.js";
import {
createWalletApiMock,
mountHook,
nullFunction,
} from "../../test-utils.js";
import { useComponentState } from "./state.js";
describe("Refund CTA states", () => {
@ -38,23 +42,28 @@ describe("Refund CTA states", () => {
talerRefundUri: undefined,
cancel: nullFunction,
onSuccess: nullFunction,
}
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -83,23 +92,28 @@ describe("Refund CTA states", () => {
} as OrderShortInfo,
});
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.accept.onClick).not.undefined;
expect(state.ignore.onClick).not.undefined;
expect(state.merchantName).eq("the merchant name");
expect(state.orderId).eq("orderId1");
expect(state.products).undefined;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.accept.onClick).not.undefined;
expect(state.ignore.onClick).not.undefined;
expect(state.merchantName).eq("the merchant name");
expect(state.orderId).eq("orderId1");
expect(state.products).undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -132,30 +146,35 @@ describe("Refund CTA states", () => {
} as OrderShortInfo,
});
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail()
if (state.error) expect.fail()
expect(state.accept.onClick).not.undefined;
expect(state.merchantName).eq("the merchant name");
expect(state.orderId).eq("orderId1");
expect(state.products).undefined;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.accept.onClick).not.undefined;
expect(state.merchantName).eq("the merchant name");
expect(state.orderId).eq("orderId1");
expect(state.products).undefined;
if (state.ignore.onClick === undefined) expect.fail();
state.ignore.onClick();
},
(state) => {
if (state.status !== "ignored") expect.fail()
if (state.error) expect.fail()
expect(state.merchantName).eq("the merchant name");
},
], TestingContext)
if (state.ignore.onClick === undefined) expect.fail();
state.ignore.onClick();
},
(state) => {
if (state.status !== "ignored") expect.fail();
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -220,42 +239,46 @@ describe("Refund CTA states", () => {
} as OrderShortInfo,
});
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "in-progress") expect.fail()
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
// expect(state.progress).closeTo(1 / 3, 0.01)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "in-progress") expect.fail();
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
// expect(state.progress).closeTo(1 / 3, 0.01)
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
},
(state) => {
if (state.status !== "in-progress") expect.fail()
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
// expect(state.progress).closeTo(2 / 3, 0.01)
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
},
(state) => {
if (state.status !== "in-progress") expect.fail();
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
// expect(state.progress).closeTo(2 / 3, 0.01)
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
},
(state) => {
if (state.status !== "ready") expect.fail()
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2"));
handler.notifyEventFromWallet(NotificationType.RefreshMelted);
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.merchantName).eq("the merchant name");
expect(state.products).undefined;
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");
});
});

View File

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

View File

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

View File

@ -36,23 +36,28 @@ describe("Tip CTA states", () => {
talerTipUri: undefined,
onCancel: nullFunction,
onSuccess: nullFunction,
}
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
({ status, error }) => {
expect(status).equals("loading-uri");
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -75,50 +80,58 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd",
onCancel: nullFunction,
onSuccess: nullFunction,
}
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({ status: "ready" });
return;
}
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
if (state.accept.onClick === undefined) expect.fail();
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") {
expect(state).eq({ status: "ready" });
return;
}
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
if (state.accept.onClick === undefined) expect.fail();
handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
state.accept.onClick();
handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
state.accept.onClick();
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
accepted: true,
exchangeBaseUrl: "exchange url",
merchantBaseUrl: "merchant url",
tipAmountEffective: "EUR:1",
walletTipId: "tip_id",
expirationTimestamp: {
t_s: 1,
},
tipAmountRaw: "",
});
handler.addWalletCallResponse(
WalletApiOperation.PrepareTip,
undefined,
{
accepted: true,
exchangeBaseUrl: "exchange url",
merchantBaseUrl: "merchant url",
tipAmountEffective: "EUR:1",
walletTipId: "tip_id",
expirationTimestamp: {
t_s: 1,
},
tipAmountRaw: "",
},
);
},
(state) => {
if (state.status !== "accepted") expect.fail();
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
},
],
TestingContext,
);
},
(state) => {
if (state.status !== "accepted") expect.fail()
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
},
], TestingContext)
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -140,25 +153,30 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd",
onCancel: nullFunction,
onSuccess: nullFunction,
}
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
//FIXME: add ignore button
},
], TestingContext)
//FIXME: add ignore button
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -181,24 +199,28 @@ describe("Tip CTA states", () => {
talerTipUri: "taler://tip/asd",
onCancel: nullFunction,
onSuccess: nullFunction,
}
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "accepted") expect.fail();
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
if (state.status !== "accepted") expect.fail();
if (state.error) expect.fail();
expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:1"));
expect(state.merchantBaseUrl).eq("merchant url");
expect(state.exchangeBaseUrl).eq("exchange url");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,7 +64,7 @@ const exchanges: ExchangeListItem[] = [
const nullFunction = async (): Promise<void> => {
null;
}
};
describe("Withdraw CTA states", () => {
it("should tell the user that the URI is missing", async () => {
@ -76,20 +76,25 @@ describe("Withdraw CTA states", () => {
onSuccess: nullFunction,
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
if (status != "uri-error") expect.fail();
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
if (status != "uri-error") expect.fail();
if (!error) expect.fail();
if (!error.hasError) expect.fail();
if (error.operational) expect.fail();
expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -110,17 +115,22 @@ describe("Withdraw CTA states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("no-exchange");
expect(error).undefined;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("no-exchange");
expect(error).undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -153,27 +163,32 @@ describe("Withdraw CTA states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.doWithdrawal.onClick).not.undefined;
},
], TestingContext)
expect(state.doWithdrawal.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -221,39 +236,44 @@ describe("Withdraw CTA states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentStateFromURI, props, [
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentStateFromURI,
props,
[
({ status }) => {
expect(status).equals("loading");
},
({ status, error }) => {
expect(status).equals("loading");
expect(error).undefined;
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.doWithdrawal.onClick).undefined;
expect(state.doWithdrawal.onClick).undefined;
state.onTosUpdate();
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
state.onTosUpdate();
},
(state) => {
expect(state.status).equals("success");
if (state.status !== "success") return;
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
expect(state.chosenAmount).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.doWithdrawal.onClick).not.undefined;
},
], TestingContext)
expect(state.doWithdrawal.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
});

View File

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

View File

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

View File

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

View File

@ -444,8 +444,8 @@ function registerTalerHeaderListener(
info: chrome.tabs.TabChangeInfo,
): Promise<void> {
if (tabId < 0) return;
const tabLocationHasBeenUpdated = info.status === "complete"
const tabTitleHasBeenUpdated = info.title !== undefined
const tabLocationHasBeenUpdated = info.status === "complete";
const tabTitleHasBeenUpdated = info.title !== undefined;
if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) {
const uri = await findTalerUriInTab(tabId);
if (!uri) return;
@ -543,26 +543,26 @@ function setAlertedIcon(): void {
interface OffscreenCanvasRenderingContext2D
extends CanvasState,
CanvasTransform,
CanvasCompositing,
CanvasImageSmoothing,
CanvasFillStrokeStyles,
CanvasShadowStyles,
CanvasFilters,
CanvasRect,
CanvasDrawPath,
CanvasUserInterface,
CanvasText,
CanvasDrawImage,
CanvasImageData,
CanvasPathDrawingStyles,
CanvasTextDrawingStyles,
CanvasPath {
CanvasTransform,
CanvasCompositing,
CanvasImageSmoothing,
CanvasFillStrokeStyles,
CanvasShadowStyles,
CanvasFilters,
CanvasRect,
CanvasDrawPath,
CanvasUserInterface,
CanvasText,
CanvasDrawImage,
CanvasImageData,
CanvasPathDrawingStyles,
CanvasTextDrawingStyles,
CanvasPath {
readonly canvas: OffscreenCanvas;
}
declare const OffscreenCanvasRenderingContext2D: {
prototype: OffscreenCanvasRenderingContext2D;
new(): OffscreenCanvasRenderingContext2D;
new (): OffscreenCanvasRenderingContext2D;
};
interface OffscreenCanvas extends EventTarget {
@ -575,7 +575,7 @@ interface OffscreenCanvas extends EventTarget {
}
declare const OffscreenCanvas: {
prototype: OffscreenCanvas;
new(width: number, height: number): OffscreenCanvas;
new (width: number, height: number): OffscreenCanvas;
};
function createCanvas(size: number): OffscreenCanvas {

View File

@ -34,18 +34,21 @@ setupI18n("en", { en: {} });
setupPlatform(chromeAPI);
describe("All the examples:", () => {
const cms = parseGroupImport({ popup, wallet, cta, mui, components })
cms.forEach(group => {
const cms = parseGroupImport({ popup, wallet, cta, mui, components });
cms.forEach((group) => {
describe(`Example for group "${group.title}:"`, () => {
group.list.forEach(component => {
group.list.forEach((component) => {
describe(`Component ${component.name}:`, () => {
component.examples.forEach(example => {
component.examples.forEach((example) => {
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 {
component: Render,
props: evaluatedProps
props: evaluatedProps,
};
}
@ -74,7 +74,7 @@ export function createExampleWithCustomContext<Props, ContextProps>(
return {
component: WithContext,
props: evaluatedProps
props: evaluatedProps,
};
}
@ -253,7 +253,7 @@ type Subscriptions = {
export function createWalletApiMock(): {
handler: MockHandler;
TestingContext: FunctionalComponent<{ children: ComponentChildren }>
TestingContext: FunctionalComponent<{ children: ComponentChildren }>;
} {
const calls = new Array<CallRecord>();
const subscriptions: Subscriptions = {};
@ -342,8 +342,8 @@ export function createWalletApiMock(): {
callback: cb
? cb
: () => {
null;
},
null;
},
});
return handler;
},
@ -358,13 +358,21 @@ export function createWalletApiMock(): {
},
};
function TestingContext({ children }: { children: ComponentChildren }): VNode {
return create(BackendProvider, {
wallet: mock.wallet,
background: mock.background,
listener: mock.listener,
function TestingContext({
children,
}: {
children: ComponentChildren;
}): VNode {
return create(
BackendProvider,
{
wallet: mock.wallet,
background: mock.background,
listener: mock.listener,
children,
},
children,
}, children)
);
}
return { handler, TestingContext };

View File

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

View File

@ -17,11 +17,11 @@
import {
canonicalizeBaseUrl,
Codec,
TalerErrorDetail
TalerErrorDetail,
} from "@gnu-taler/taler-util";
import {
codecForSyncTermsOfServiceResponse,
WalletApiOperation
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
import { useEffect, useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
@ -106,47 +106,50 @@ function useUrlState<T>(
constHref == undefined
? undefined
: async () => {
const req = await fetch(constHref).catch((e) => {
return setState({
status: "network-error",
href: constHref,
const req = await fetch(constHref).catch((e) => {
return setState({
status: "network-error",
href: constHref,
});
});
});
if (!req) return;
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 });
}
},
[host, path],
);
return state;
}
export function useComponentState(
{ currency, onBack, onComplete, onPaymentRequired }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
currency,
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);
@ -223,8 +226,8 @@ export function useComponentState(
!urlState || urlState.status !== "ok" || !name
? undefined
: async () => {
setShowConfirm(true);
},
setShowConfirm(true);
},
},
urlOk: urlState?.status === "ok",
url: {

View File

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

View File

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

View File

@ -21,7 +21,7 @@ import {
KnownBankAccountsInfo,
parsePaytoUri,
PaytoUri,
stringifyPaytoUri
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
@ -29,10 +29,13 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js";
export function useComponentState(
{ amount: amountStr, currency: currencyStr, onCancel, onSuccess }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
amount: amountStr,
currency: currencyStr,
onCancel,
onSuccess,
}: Props): State {
const api = useBackendContext();
const parsed = amountStr === undefined ? undefined : Amounts.parse(amountStr);
const currency = parsed !== undefined ? parsed.currency : currencyStr;
@ -55,8 +58,8 @@ export function useComponentState(
parsed !== undefined
? parsed
: currency !== undefined
? Amounts.zeroOfCurrency(currency)
: undefined;
? Amounts.zeroOfCurrency(currency)
: undefined;
// const [accountIdx, setAccountIdx] = useState<number>(0);
const [amount, setAmount] = useState<AmountJson>(initialValue ?? ({} as any));
const [selectedAccount, setSelectedAccount] = useState<PaytoUri>();
@ -162,7 +165,11 @@ export function useComponentState(
async function updateAmount(newAmount: AmountJson): Promise<void> {
// const parsed = Amounts.parse(`${currency}:${numStr}`);
try {
const result = await getFeeForAmount(currentAccount, newAmount, api.wallet);
const result = await getFeeForAmount(
currentAccount,
newAmount,
api.wallet,
);
setAmount(newAmount);
setFee(result);
} catch (e) {
@ -185,8 +192,8 @@ export function useComponentState(
const amountError = !isDirty
? undefined
: Amounts.cmp(balance, amount) === -1
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
: undefined;
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
: undefined;
const unableToDeposit =
Amounts.isZero(totalToDeposit) || //deposit may be zero because of fee

View File

@ -23,14 +23,12 @@ import {
Amounts,
DepositGroupFees,
parsePaytoUri,
stringifyPaytoUri
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai";
import { tests } from "../../../../web-util/src/index.browser.js";
import {
createWalletApiMock, nullFunction
} from "../../test-utils.js";
import { createWalletApiMock, nullFunction } from "../../test-utils.js";
import { useComponentState } from "./state.js";
@ -71,16 +69,21 @@ describe("DepositPage states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("no-enough-balance");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("no-enough-balance");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -107,16 +110,21 @@ describe("DepositPage states", () => {
},
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("no-accounts");
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("no-accounts");
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -161,24 +169,29 @@ describe("DepositPage states", () => {
withoutFee(),
);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
},
], TestingContext)
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(ibanPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -218,37 +231,42 @@ describe("DepositPage states", () => {
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.account.onChange).not.undefined;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.account.onChange).not.undefined;
state.account.onChange!(accountSelected);
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.depositHandler.onClick).undefined;
},
], TestingContext)
state.account.onChange!(accountSelected);
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.depositHandler.onClick).undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
@ -292,52 +310,58 @@ describe("DepositPage states", () => {
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.account.onChange).not.undefined;
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(stringifyPaytoUri(talerBankPayto.uri));
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`));
expect(state.account.onChange).not.undefined;
state.account.onChange!(accountSelected);
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
state.account.onChange!(accountSelected);
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:0"));
expect(state.depositHandler.onClick).undefined;
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
expect(state.amount.onInput).not.undefined;
if (!state.amount.onInput) return;
state.amount.onInput(Amounts.parseOrThrow("EUR:10"));
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
expect(state.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`));
expect(state.depositHandler.onClick).not.undefined;
},
], TestingContext)
expect(state.amount.onInput).not.undefined;
if (!state.amount.onInput) return;
state.amount.onInput(Amounts.parseOrThrow("EUR:10"));
},
(state) => {
if (state.status !== "ready") expect.fail();
expect(state.cancelHandler.onClick).not.undefined;
expect(state.currency).eq(currency);
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
expect(state.totalToDeposit).deep.eq(
Amounts.parseOrThrow(`${currency}:7`),
);
expect(state.depositHandler.onClick).not.undefined;
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
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 { Contact, Props, State } from "./index.js";
export function useComponentState(
props: Props,
): RecursiveState<State> {
const api = useBackendContext()
export function useComponentState(props: Props): RecursiveState<State> {
const api = useBackendContext();
const parsedInitialAmount = !props.amount
? undefined
: Amounts.parse(props.amount);
@ -41,22 +39,22 @@ export function useComponentState(
const previous: Contact[] = true
? []
: [
{
name: "International Bank",
icon_type: 'bank',
description: "account ending with 3454",
},
{
name: "Max",
icon_type: 'bank',
description: "account ending with 3454",
},
{
name: "Alex",
icon_type: 'bank',
description: "account ending with 3454",
},
];
{
name: "International Bank",
icon_type: "bank",
description: "account ending with 3454",
},
{
name: "Max",
icon_type: "bank",
description: "account ending with 3454",
},
{
name: "Alex",
icon_type: "bank",
description: "account ending with 3454",
},
];
if (!amount) {
return () => {
@ -114,15 +112,15 @@ export function useComponentState(
onClick: invalid
? undefined
: async () => {
props.goToWalletBankDeposit(currencyAndAmount);
},
props.goToWalletBankDeposit(currencyAndAmount);
},
},
goToWallet: {
onClick: invalid
? undefined
: async () => {
props.goToWalletWalletSend(currencyAndAmount);
},
props.goToWalletWalletSend(currencyAndAmount);
},
},
amountHandler: {
onInput: async (s) => setAmount(s),
@ -144,15 +142,15 @@ export function useComponentState(
onClick: invalid
? undefined
: async () => {
props.goToWalletManualWithdraw(currencyAndAmount);
},
props.goToWalletManualWithdraw(currencyAndAmount);
},
},
goToWallet: {
onClick: invalid
? undefined
: async () => {
props.goToWalletWalletInvoice(currencyAndAmount);
},
props.goToWalletWalletInvoice(currencyAndAmount);
},
},
amountHandler: {
onInput: async (s) => setAmount(s),

View File

@ -23,7 +23,7 @@ import {
Amounts,
ExchangeEntryStatus,
ExchangeListItem,
ExchangeTosStatus
ExchangeTosStatus,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { expect } from "chai";
@ -59,33 +59,39 @@ describe("Destination selection states", () => {
goToWalletWalletInvoice: nullFunction,
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "select-currency") expect.fail();
if (state.error) expect.fail();
expect(state.currencies).deep.eq({
ARS: "ARS",
"": "Select a currency",
});
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
({ status }) => {
expect(status).equal("loading");
},
(state) => {
if (state.status !== "select-currency") expect.fail();
if (state.error) expect.fail();
expect(state.currencies).deep.eq({
ARS: "ARS",
"": "Select a currency",
});
state.onCurrencySelected(exchangeArs.currency!);
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.goToBank.onClick).eq(undefined);
expect(state.goToWallet.onClick).eq(undefined);
state.onCurrencySelected(exchangeArs.currency!);
},
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.goToBank.onClick).eq(undefined);
expect(state.goToWallet.onClick).eq(undefined);
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:0"));
},
], TestingContext)
expect(state.amountHandler.value).deep.eq(
Amounts.parseOrThrow("ARS:0"),
);
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
it("should be possible to start with an amount specified in request params", async () => {
@ -98,22 +104,28 @@ describe("Destination selection states", () => {
amount: "ARS:2",
};
const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
// ({ status }) => {
// expect(status).equal("loading");
// },
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.goToBank.onClick).not.eq(undefined);
expect(state.goToWallet.onClick).not.eq(undefined);
const hookBehavior = await tests.hookBehaveLikeThis(
useComponentState,
props,
[
// ({ status }) => {
// expect(status).equal("loading");
// },
(state) => {
if (state.status !== "ready") expect.fail();
if (state.error) expect.fail();
expect(state.goToBank.onClick).not.eq(undefined);
expect(state.goToWallet.onClick).not.eq(undefined);
expect(state.amountHandler.value).deep.eq(Amounts.parseOrThrow("ARS:2"));
},
], TestingContext)
expect(state.amountHandler.value).deep.eq(
Amounts.parseOrThrow("ARS:2"),
);
},
],
TestingContext,
);
expect(hookBehavior).deep.equal({ result: "ok" })
expect(hookBehavior).deep.equal({ result: "ok" });
expect(handler.getCallingQueueState()).eq("empty");
});
});

View File

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

View File

@ -17,17 +17,20 @@
import { DenomOperationMap, FeeDescription } from "@gnu-taler/taler-util";
import {
createPairTimeline,
WalletApiOperation
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js";
export function useComponentState(
{ onCancel, onSelection, list: exchanges, currentExchange }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
onCancel,
onSelection,
list: exchanges,
currentExchange,
}: Props): State {
const api = useBackendContext();
const initialValue = exchanges.findIndex(
(e) => e.exchangeBaseUrl === currentExchange,
);
@ -52,14 +55,14 @@ export function useComponentState(
const selected = !selectedExchange
? undefined
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
});
exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
});
const original = !initialExchange
? undefined
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, {
exchangeBaseUrl: initialExchange.exchangeBaseUrl,
});
exchangeBaseUrl: initialExchange.exchangeBaseUrl,
});
return {
exchanges,

View File

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

View File

@ -17,7 +17,7 @@
import {
KnownBankAccountsInfo,
parsePaytoUri,
stringifyPaytoUri
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useState } from "preact/hooks";
@ -25,10 +25,12 @@ import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { AccountByType, Props, State } from "./index.js";
export function useComponentState(
{ currency, onAccountAdded, onCancel }: Props,
): State {
const api = useBackendContext()
export function useComponentState({
currency,
onAccountAdded,
onCancel,
}: Props): State {
const api = useBackendContext();
const hook = useAsyncAsHook(() =>
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 { LoadingUriView, ReadyView } from "./views.js";
export type Props = object
export type Props = object;
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";
export function useComponentState(p: Props): State {
const api = useBackendContext()
const api = useBackendContext();
const hook = useAsyncAsHook(async () => {
return await api.wallet.call(
WalletApiOperation.GetUserAttentionRequests,

View File

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