taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

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:
Mpackages/wallet-webui/test/screens.test.tsx | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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");