commit d5096d47898b36a592b7282b6fdc7ec3dcf98ab3
parent 1f74e51fdcb6d61395b079d2738bea2029cc1bd8
Author: Florian Dold <dold@taler.net>
Date: Tue, 1 Sep 2026 22:53:30 +0200
wallet web UI: test recoverable sharing and handoff
Diffstat:
1 file changed, 102 insertions(+), 0 deletions(-)
diff --git a/packages/wallet-webui/test/screens.test.tsx b/packages/wallet-webui/test/screens.test.tsx
@@ -11,6 +11,7 @@ import { DepositScreen } from "../src/screens/DepositScreen.js";
import { depositDestinationView } from "../src/routes/deposit-model.js";
import { PeerCreateScreen } from "../src/screens/PeerCreateScreen.js";
import { PeerReceiveScreen } from "../src/screens/PeerReceiveScreen.js";
+import { PeerShareScreen } from "../src/screens/PeerShareScreen.js";
import { BankAccountsScreen } from "../src/screens/BankAccountsScreen.js";
import { ExchangeDetailScreen } from "../src/screens/ExchangeDetailScreen.js";
import { TermsScreen } from "../src/screens/TermsScreen.js";
@@ -1531,12 +1532,22 @@ test("payment handoff is confirmed even when the payment choice is unavailable",
test("payment handoff shows a QR code and has a neutral completion screen", async () => {
const window = installDom();
const { render, cleanup } = await import("@testing-library/preact");
+ const userEvent = (await import("@testing-library/user-event"))
+ .default as unknown as {
+ setup(options: { document: Document }): {
+ click(element: Element): Promise<void>;
+ };
+ };
const uri = "taler://pay/merchant.example/orders/ORDER";
+ let reclaimed = false;
const view = render(
<main>
<PaymentScreen
state="handoff"
talerPayUri={uri}
+ onReclaim={() => {
+ reclaimed = true;
+ }}
onSelectChoice={() => {}}
onConfirm={() => {}}
onWithdraw={() => {}}
@@ -1550,6 +1561,10 @@ test("payment handoff shows a QR code and has a neutral completion screen", asyn
name: "QR code to continue the payment in another wallet",
}),
);
+ await userEvent
+ .setup({ document: window.document as unknown as Document })
+ .click(view.getByRole("button", { name: "Continue with this wallet" }));
+ assert.equal(reclaimed, true);
view.rerender(
<main>
<PaymentScreen
@@ -1573,6 +1588,93 @@ test("payment handoff shows a QR code and has a neutral completion screen", asyn
await window.happyDOM.abort();
});
+test("payment handoff can be reversed while it is being prepared", async () => {
+ const window = installDom();
+ const { render, cleanup } = await import("@testing-library/preact");
+ const userEvent = (await import("@testing-library/user-event"))
+ .default as unknown as {
+ setup(options: { document: Document }): {
+ click(element: Element): Promise<void>;
+ };
+ };
+ let keptHere = false;
+ const view = render(
+ <main>
+ <PaymentScreen
+ state="ready"
+ merchantName="Example Merchant"
+ choices={[
+ {
+ index: 0,
+ description: "Museum entry",
+ amountRaw: "CHF:5",
+ payable: true,
+ inputs: [],
+ outputs: [],
+ },
+ ]}
+ selectedChoice={0}
+ unclaiming
+ onSelectChoice={() => {}}
+ onConfirm={() => {}}
+ onUnclaim={() => {}}
+ onKeepHere={() => {
+ keptHere = true;
+ }}
+ onWithdraw={() => {}}
+ onCancel={() => {}}
+ onOpenFulfillment={() => {}}
+ />
+ </main>,
+ );
+ assert.ok(view.getByText("Preparing the payment for another wallet…"));
+ await userEvent
+ .setup({ document: window.document as unknown as Document })
+ .click(view.getByRole("button", { name: "Continue with this wallet" }));
+ assert.equal(keptHere, true);
+ cleanup();
+ await window.happyDOM.abort();
+});
+
+test("peer sharing reports delays and exposes recoverable retries", async () => {
+ const window = installDom();
+ const { render, cleanup } = await import("@testing-library/preact");
+ const userEvent = (await import("@testing-library/user-event"))
+ .default as unknown as {
+ setup(options: { document: Document }): {
+ click(element: Element): Promise<void>;
+ };
+ };
+ let retried = false;
+ const base = {
+ mode: "send" as const,
+ onCopy: () => {},
+ onDone: () => {},
+ };
+ const view = render(<PeerShareScreen {...base} loading />);
+ assert.ok(view.getByText("Preparing a secure share link…"));
+ view.rerender(<PeerShareScreen {...base} loading delayed />);
+ assert.ok(view.getByText("Waiting for a response from the exchange…"));
+ view.rerender(
+ <PeerShareScreen
+ {...base}
+ loading={false}
+ error={{ message: "The exchange did not respond." }}
+ retryable
+ onRetry={() => {
+ retried = true;
+ }}
+ />,
+ );
+ assert.ok(view.getByText(/wallet will retry automatically/i));
+ await userEvent
+ .setup({ document: window.document as unknown as Document })
+ .click(view.getByRole("button", { name: "Try now" }));
+ assert.equal(retried, true);
+ cleanup();
+ await window.happyDOM.abort();
+});
+
test("a legally paused payment can be resumed or cancelled", async () => {
const window = installDom();
const { render, cleanup } = await import("@testing-library/preact");