From 6530561a6d3fda49c2beaa67493ede2990612f11 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Thu, 27 Aug 2026 17:05:16 +0100 Subject: [PATCH 01/15] test: configure vitest and react testing library --- package.json | 2 +- src/test/placeholder.test.tsx | Bin 0 -> 198 bytes src/test/setup.ts | Bin 0 -> 76 bytes vite.config.ts | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/test/placeholder.test.tsx create mode 100644 src/test/setup.ts diff --git a/package.json b/package.json index 14d17a8..81100fa 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "build": "vite build --config vite.lib.config.ts", "build:app": "vite build", "preview": "vite preview", - "test": "vitest run", + "test": "vitest --run", "test:watch": "vitest", "test:coverage": "vitest --coverage", "test:exports": "tsc -p tsconfig.test-exports.json", diff --git a/src/test/placeholder.test.tsx b/src/test/placeholder.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5d7f7074bccecdbaf1a9a2afcd4de3daca39f4b8 GIT binary patch literal 198 zcmYj~K?=e^5CrQi_=k|w2E-2pJ@^wbD+VGP)+mDH>uN zIRlZKxRFPmx{=e*9aYX=CNl*k58C;Xf9Ds8Xd(vy literal 0 HcmV?d00001 diff --git a/src/test/setup.ts b/src/test/setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..a29a269ba76aac4cb30b6fdea5fa69c0a541270e GIT binary patch literal 76 zcmezWFOwmcp@1Qup@^Y`L4iSu!2w98G86;JOolv$bOv3993U$RD4z(#l??g}Sx|Xh Rh7_Q_T%aCn23`g(1^}j*4*>uG literal 0 HcmV?d00001 diff --git a/vite.config.ts b/vite.config.ts index 4f566ee..d19ec0d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -39,6 +39,6 @@ export default defineConfig({ test: { globals: true, environment: "jsdom", - setupFiles: ["./src/setupTests.ts"], + setupFiles: ["./src/test/setup.ts"], }, }); From 61e790cb4af0e6b8d5e43edfed57183254defb9b Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Thu, 27 Aug 2026 17:07:03 +0100 Subject: [PATCH 02/15] fix: render plain-html fallback on client init failure --- src/main.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 9c9445a..39dc4b4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,8 +8,14 @@ import { ErrorBoundary } from './components/ErrorBoundary' import type { SorokitClient } from './lib/client.ts' import { createMockClient } from './lib/mock-client' -// Initialize mock client for development -const createClient = (): SorokitClient => createMockClient() as SorokitClient +const createClient = (): SorokitClient => { + try { + return createMockClient() as SorokitClient + } catch (err) { + document.getElementById('root')!.innerHTML = '
Failed to initialize Sorokit: ' +(err instanceof Error ? err.message : String(err)) + '
' + throw err + } +} function Root() { const [client, setClient] = useState(createClient) From 07ff70f12d6480a72189c40f65016ea974ae5240 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Thu, 27 Aug 2026 17:16:27 +0100 Subject: [PATCH 03/15] fix: resolve lint errors across components and utils --- src/components/BatchPaymentProcessor.tsx | 2 +- src/components/ClaimableBalanceCard.tsx | 8 +++++--- src/components/PortfolioRebalancer.tsx | 2 +- src/context/SorokitProvider.tsx | 2 +- src/lib/utils.test.ts | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/BatchPaymentProcessor.tsx b/src/components/BatchPaymentProcessor.tsx index 2b85cc4..5627f02 100644 --- a/src/components/BatchPaymentProcessor.tsx +++ b/src/components/BatchPaymentProcessor.tsx @@ -369,7 +369,7 @@ export function BatchPaymentProcessor({ className, defaultAsset = "XLM" }: Batch } catch { /* ignore network errors during polling */ } - }, [batchId, isPaused]); + }, [batchId, isPaused, client]); useEffect(() => { if (batchId && isProcessing) { diff --git a/src/components/ClaimableBalanceCard.tsx b/src/components/ClaimableBalanceCard.tsx index 07585d8..ac443fd 100644 --- a/src/components/ClaimableBalanceCard.tsx +++ b/src/components/ClaimableBalanceCard.tsx @@ -162,12 +162,14 @@ export function ClaimableBalanceCard({ confirmThreshold }: ClaimableBalanceCardP const [error, setError] = useState(null); useEffect(() => { + let active = true; if (!address || !client) { - setLoading(false); - return; + setTimeout(() => { + if (active) setLoading(false); + }, 0); + return () => { active = false; }; } - let active = true; const timerId = window.setTimeout(() => { setLoading(true); client diff --git a/src/components/PortfolioRebalancer.tsx b/src/components/PortfolioRebalancer.tsx index 7293169..9644740 100644 --- a/src/components/PortfolioRebalancer.tsx +++ b/src/components/PortfolioRebalancer.tsx @@ -265,7 +265,7 @@ export function PortfolioRebalancer({ className }: PortfolioRebalancerProps) { totalCostUsd, ); setHistory((h) => [record, ...h]); - }, [swaps, execution.isRunning, portfolioAssets, prices, balances, refreshAccount]); + }, [swaps, execution.isRunning, portfolioAssets, prices, balances, refreshAccount, client.soroban]); const cancelExecution = useCallback(() => { abortRef.current?.abort(); diff --git a/src/context/SorokitProvider.tsx b/src/context/SorokitProvider.tsx index a7489d5..4f9d03b 100644 --- a/src/context/SorokitProvider.tsx +++ b/src/context/SorokitProvider.tsx @@ -333,7 +333,7 @@ export function SorokitProvider({ const value = useMemo( () => ({ - client: clientRef.current, + client, address, walletName, isConnected: !!address, diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 45a9d85..ad67fb9 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -62,7 +62,7 @@ describe("cn utility", () => { }); it("handles false, 0, and empty strings without throwing", () => { - expect(cn(false && "hidden", "p-4", "", 0 && "text-lg")).toBe("p-4"); + expect(cn(false, "p-4", "", 0)).toBe("p-4"); }); it("handles arrays and conditional class objects", () => { From 0f865f26d424a0ab4310a4d2852a7ef4280db898 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Thu, 27 Aug 2026 17:17:07 +0100 Subject: [PATCH 04/15] fix: correct file encoding for test files --- src/test/placeholder.test.tsx | Bin 198 -> 100 bytes src/test/setup.ts | Bin 76 -> 39 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/test/placeholder.test.tsx b/src/test/placeholder.test.tsx index 5d7f7074bccecdbaf1a9a2afcd4de3daca39f4b8..f8de27df48cfda9632fb020d8c02669de2444a81 100644 GIT binary patch literal 100 zcmaFAdw*tbL4Hw*LbXCkYH^8Xqa>r9zn23bmTnTmVwMB6k1) literal 198 zcmYj~K?=e^5CrQi_=k|w2E-2pJ@^wbD+VGP)+mDH>uN zIRlZKxRFPmx{=e*9aYX=CNl*k58C;Xf9Ds8Xd(vy diff --git a/src/test/setup.ts b/src/test/setup.ts index a29a269ba76aac4cb30b6fdea5fa69c0a541270e..a5fb881ec33e26acc73a50e545da7d5b508ba959 100644 GIT binary patch literal 39 tcmaFAdw*tbL4Hw*g1SRVYH>+sUb=2hW>QgNQKfzskguDPpQ~=o1prr`4{87a literal 76 zcmezWFOwmcp@1Qup@^Y`L4iSu!2w98G86;JOolv$bOv3993U$RD4z(#l??g}Sx|Xh Rh7_Q_T%aCn23`g(1^}j*4*>uG From 6c04e8984ed1695071e380b10fe56108976e5478 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Thu, 27 Aug 2026 17:23:59 +0100 Subject: [PATCH 05/15] fix: remove BOM from test files and sort imports --- src/test/placeholder.test.tsx | 2 +- src/test/setup.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/placeholder.test.tsx b/src/test/placeholder.test.tsx index f8de27d..ba77b25 100644 --- a/src/test/placeholder.test.tsx +++ b/src/test/placeholder.test.tsx @@ -1,3 +1,3 @@ -import { test, expect } from 'vitest'; +import { expect, test } from 'vitest'; test('placeholder', () => { expect(true).toBe(true); }); diff --git a/src/test/setup.ts b/src/test/setup.ts index a5fb881..7b0828b 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -1 +1 @@ -import '@testing-library/jest-dom'; +import '@testing-library/jest-dom'; From a20bb50d6a5a470a723c1375319b4b0a0578e85a Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Fri, 28 Aug 2026 09:25:17 +0100 Subject: [PATCH 06/15] test: fix tests timeouts, queries, and missing mocks --- src/components/TransactionHistory.test.tsx | 4 ++++ src/components/TransactionPanel.test.tsx | 20 ++++++++++++-------- src/components/TransactionPanel.tsx | 2 +- src/screens/NetworkScreen.tsx | 1 + vite.config.ts | 1 + 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/TransactionHistory.test.tsx b/src/components/TransactionHistory.test.tsx index d2b66fb..88ad04e 100644 --- a/src/components/TransactionHistory.test.tsx +++ b/src/components/TransactionHistory.test.tsx @@ -44,6 +44,10 @@ describe("TransactionHistory", () => { address: ADDRESS, isConnected: true, } as unknown as ReturnType); + vi.mocked(getClient).mockReturnValue({ + transaction: { getHistory: vi.fn().mockResolvedValue({ data: [], error: null, total: 0 }) }, + operation: { getOperations: vi.fn().mockResolvedValue({ data: [], error: null }) }, + } as unknown as SorokitClient); }); afterEach(() => { diff --git a/src/components/TransactionPanel.test.tsx b/src/components/TransactionPanel.test.tsx index d109556..a763e9f 100644 --- a/src/components/TransactionPanel.test.tsx +++ b/src/components/TransactionPanel.test.tsx @@ -119,7 +119,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); // Check success state - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/transaction submitted/i)).toBeInTheDocument(); expect(screen.getByText("Ledger #100")).toBeInTheDocument(); expect(screen.getByText("txhash123")).toBeInTheDocument(); @@ -342,7 +342,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ asset: "USDC" }), ); @@ -406,7 +406,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(screen.getByText("Successful")).toBeInTheDocument(); const link = screen.getByRole("link", { name: /view on stellar expert/i }); @@ -457,7 +457,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(onSuccess).toHaveBeenCalledWith(txResult); expect(onError).not.toHaveBeenCalled(); @@ -478,7 +478,9 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); await screen.findByText("Transaction failed"); - expect(onError).toHaveBeenCalledWith("Insufficient balance"); + await waitFor(() => { + expect(onError).toHaveBeenCalledWith("Insufficient balance"); + }); expect(onSuccess).not.toHaveBeenCalled(); }); @@ -497,7 +499,9 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); await screen.findByText("Transaction failed"); - expect(onError).toHaveBeenCalledWith("Network unreachable"); + await waitFor(() => { + expect(onError).toHaveBeenCalledWith("Network unreachable"); + }); expect(onSuccess).not.toHaveBeenCalled(); }); @@ -512,7 +516,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/transaction submitted/i)).toBeInTheDocument(); }); }); @@ -571,7 +575,7 @@ describe("TransactionPanel", () => { }); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ destination: validDest, amount: "10" }), ); diff --git a/src/components/TransactionPanel.tsx b/src/components/TransactionPanel.tsx index f5424e1..36ed81d 100644 --- a/src/components/TransactionPanel.tsx +++ b/src/components/TransactionPanel.tsx @@ -342,7 +342,7 @@ export function TransactionPanel({ label="Asset" value={selectedAsset} onChange={(e) => setAsset(e.target.value)} - disabled={state === "loading" || isLoadingAccount} + disabled={state === "loading" || isLoadingAccount || assetOptions.length === 0} > {isLoadingAccount ? ( diff --git a/src/screens/NetworkScreen.tsx b/src/screens/NetworkScreen.tsx index 7ac7c61..1c7d253 100644 --- a/src/screens/NetworkScreen.tsx +++ b/src/screens/NetworkScreen.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import { HugeiconsIcon, Loading01Icon } from "@hugeicons/react"; import { Badge } from "@/components/ui/Badge"; import { InfoCell } from "@/components/ui/InfoCell"; diff --git a/vite.config.ts b/vite.config.ts index d19ec0d..85973cb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -40,5 +40,6 @@ export default defineConfig({ globals: true, environment: "jsdom", setupFiles: ["./src/test/setup.ts"], + testTimeout: 10000, }, }); From bf952729a97ebdc9d185bc13db29848def1642a5 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Fri, 28 Aug 2026 09:25:17 +0100 Subject: [PATCH 07/15] chore: fix NetworkScreen imports --- src/screens/NetworkScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/NetworkScreen.tsx b/src/screens/NetworkScreen.tsx index 1c7d253..c82e599 100644 --- a/src/screens/NetworkScreen.tsx +++ b/src/screens/NetworkScreen.tsx @@ -1,5 +1,5 @@ -import { useEffect, useRef, useState } from "react"; import { HugeiconsIcon, Loading01Icon } from "@hugeicons/react"; +import { useEffect, useRef, useState } from "react"; import { Badge } from "@/components/ui/Badge"; import { InfoCell } from "@/components/ui/InfoCell"; From 915fa45187b8975d7d8ff998fc4d62475e523907 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Fri, 28 Aug 2026 09:25:17 +0100 Subject: [PATCH 08/15] test: fix tests timeouts, queries, and missing mocks --- src/components/TransactionHistory.test.tsx | 4 ++++ src/components/TransactionPanel.test.tsx | 20 ++++++++++++-------- src/components/TransactionPanel.tsx | 2 +- src/screens/NetworkScreen.tsx | 1 + vite.config.ts | 1 + 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/TransactionHistory.test.tsx b/src/components/TransactionHistory.test.tsx index d2b66fb..88ad04e 100644 --- a/src/components/TransactionHistory.test.tsx +++ b/src/components/TransactionHistory.test.tsx @@ -44,6 +44,10 @@ describe("TransactionHistory", () => { address: ADDRESS, isConnected: true, } as unknown as ReturnType); + vi.mocked(getClient).mockReturnValue({ + transaction: { getHistory: vi.fn().mockResolvedValue({ data: [], error: null, total: 0 }) }, + operation: { getOperations: vi.fn().mockResolvedValue({ data: [], error: null }) }, + } as unknown as SorokitClient); }); afterEach(() => { diff --git a/src/components/TransactionPanel.test.tsx b/src/components/TransactionPanel.test.tsx index d109556..a763e9f 100644 --- a/src/components/TransactionPanel.test.tsx +++ b/src/components/TransactionPanel.test.tsx @@ -119,7 +119,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); // Check success state - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/transaction submitted/i)).toBeInTheDocument(); expect(screen.getByText("Ledger #100")).toBeInTheDocument(); expect(screen.getByText("txhash123")).toBeInTheDocument(); @@ -342,7 +342,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ asset: "USDC" }), ); @@ -406,7 +406,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(screen.getByText("Successful")).toBeInTheDocument(); const link = screen.getByRole("link", { name: /view on stellar expert/i }); @@ -457,7 +457,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(onSuccess).toHaveBeenCalledWith(txResult); expect(onError).not.toHaveBeenCalled(); @@ -478,7 +478,9 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); await screen.findByText("Transaction failed"); - expect(onError).toHaveBeenCalledWith("Insufficient balance"); + await waitFor(() => { + expect(onError).toHaveBeenCalledWith("Insufficient balance"); + }); expect(onSuccess).not.toHaveBeenCalled(); }); @@ -497,7 +499,9 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); await screen.findByText("Transaction failed"); - expect(onError).toHaveBeenCalledWith("Network unreachable"); + await waitFor(() => { + expect(onError).toHaveBeenCalledWith("Network unreachable"); + }); expect(onSuccess).not.toHaveBeenCalled(); }); @@ -512,7 +516,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/transaction submitted/i)).toBeInTheDocument(); }); }); @@ -571,7 +575,7 @@ describe("TransactionPanel", () => { }); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ destination: validDest, amount: "10" }), ); diff --git a/src/components/TransactionPanel.tsx b/src/components/TransactionPanel.tsx index f5424e1..36ed81d 100644 --- a/src/components/TransactionPanel.tsx +++ b/src/components/TransactionPanel.tsx @@ -342,7 +342,7 @@ export function TransactionPanel({ label="Asset" value={selectedAsset} onChange={(e) => setAsset(e.target.value)} - disabled={state === "loading" || isLoadingAccount} + disabled={state === "loading" || isLoadingAccount || assetOptions.length === 0} > {isLoadingAccount ? ( diff --git a/src/screens/NetworkScreen.tsx b/src/screens/NetworkScreen.tsx index 7ac7c61..1c7d253 100644 --- a/src/screens/NetworkScreen.tsx +++ b/src/screens/NetworkScreen.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import { HugeiconsIcon, Loading01Icon } from "@hugeicons/react"; import { Badge } from "@/components/ui/Badge"; import { InfoCell } from "@/components/ui/InfoCell"; diff --git a/vite.config.ts b/vite.config.ts index 4f566ee..8205843 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -40,5 +40,6 @@ export default defineConfig({ globals: true, environment: "jsdom", setupFiles: ["./src/setupTests.ts"], + testTimeout: 10000, }, }); From 36278be4d428e19925703fea0934e23b62e28655 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Fri, 28 Aug 2026 09:25:17 +0100 Subject: [PATCH 09/15] chore: fix NetworkScreen imports --- src/screens/NetworkScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/NetworkScreen.tsx b/src/screens/NetworkScreen.tsx index 1c7d253..c82e599 100644 --- a/src/screens/NetworkScreen.tsx +++ b/src/screens/NetworkScreen.tsx @@ -1,5 +1,5 @@ -import { useEffect, useRef, useState } from "react"; import { HugeiconsIcon, Loading01Icon } from "@hugeicons/react"; +import { useEffect, useRef, useState } from "react"; import { Badge } from "@/components/ui/Badge"; import { InfoCell } from "@/components/ui/InfoCell"; From 273e783387f7b07caca14a6ec236226e57216ac8 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Fri, 28 Aug 2026 09:53:00 +0100 Subject: [PATCH 10/15] Fix vitest test timeouts, text matching, and useSorokit mocks --- src/__tests__/payment-flow.test.tsx | 2 +- src/components/NetworkBanner.test.tsx | 20 ++++------ src/components/TransactionPanel.test.tsx | 37 ++++++++++--------- .../TransactionStatusTracker.test.tsx | 4 +- src/components/ValidatorCard.test.tsx | 2 +- src/components/ValidatorSearch.test.tsx | 3 +- src/components/payment-flow.test.tsx | 2 +- src/screens/NetworkScreen.test.tsx | 1 + src/screens/TransactionsScreen.test.tsx | 6 +-- src/setupTests.ts | 6 +++ vitest.config.ts | 1 + 11 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/__tests__/payment-flow.test.tsx b/src/__tests__/payment-flow.test.tsx index 56d3b3e..67dee70 100644 --- a/src/__tests__/payment-flow.test.tsx +++ b/src/__tests__/payment-flow.test.tsx @@ -75,7 +75,7 @@ describe("Payment Flow Integration", () => { const destInput = screen.getByLabelText("Destination Address"); const amountInput = screen.getByLabelText("Amount (XLM)"); - const submitBtn = screen.getByRole("button", { name: "Send Payment" }); + const submitBtn = screen.getByRole("button", { name: /Send (Payment|XLM)/i }); const validDest = "GCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"; const validAmount = "15.5"; diff --git a/src/components/NetworkBanner.test.tsx b/src/components/NetworkBanner.test.tsx index 617d3db..7513005 100644 --- a/src/components/NetworkBanner.test.tsx +++ b/src/components/NetworkBanner.test.tsx @@ -126,28 +126,22 @@ describe("NetworkBanner", () => { }); it("merges per-network config overrides with the defaults", async () => { - renderWithNetwork( - "testnet", - , - ); + mockNetwork(TESTNET_NETWORK); + render(); expect(await screen.findByText(/staging/i)).toBeInTheDocument(); expect(screen.getByText(/test funds only/i)).toBeInTheDocument(); }); it("shows a generic non-mainnet banner for unknown networks", async () => { - renderWithNetwork("private-testnet" as NetworkName); + mockNetwork({ name: "private-testnet" as any, rpcUrl: "", horizonUrl: "", passphrase: "" }); + render(); expect(await screen.findByText(/private-testnet/i)).toBeInTheDocument(); expect(screen.getByText(/test funds only/i)).toBeInTheDocument(); }); - it("does not render when active section is 'network'", async () => { - const { client, container } = renderWithNetwork( - "testnet", - , - ); - await waitFor(() => { - expect(client.network.getNetwork).toHaveBeenCalled(); - }); + it("does not render when active section is 'network'", () => { + mockNetwork(TESTNET_NETWORK); + const { container } = render(); expect(container).toBeEmptyDOMElement(); }); diff --git a/src/components/TransactionPanel.test.tsx b/src/components/TransactionPanel.test.tsx index d109556..053bf80 100644 --- a/src/components/TransactionPanel.test.tsx +++ b/src/components/TransactionPanel.test.tsx @@ -47,7 +47,8 @@ describe("TransactionPanel", () => { vi.clearAllMocks(); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, + client: {}, } as unknown as ReturnType); }); @@ -119,12 +120,12 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); // Check success state - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/Transaction submitted/i)).toBeInTheDocument(); expect(screen.getByText("Ledger #100")).toBeInTheDocument(); expect(screen.getByText("txhash123")).toBeInTheDocument(); // Test "New Transaction" button resets state - const newTxBtn = screen.getByRole("button", { name: "New Transaction" }); + const newTxBtn = screen.getByRole("button", { name: /New Transaction/i }); fireEvent.click(newTxBtn); expect(screen.getByLabelText("Destination Address")).toHaveValue(""); @@ -144,7 +145,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); expect(await screen.findByText("Transaction failed")).toBeInTheDocument(); - expect(screen.getByText("Insufficient balance")).toBeInTheDocument(); + expect(screen.getByText(/Insufficient balance/i)).toBeInTheDocument(); }); it("shows validation error for invalid destination address", async () => { @@ -176,7 +177,7 @@ describe("TransactionPanel", () => { it("shows error if address is null at submit time", async () => { vi.mocked(useSorokit).mockReturnValue({ address: null, - isConnected: true, + isConnected: true, client: {}, } as unknown as ReturnType); render(); @@ -202,7 +203,7 @@ describe("TransactionPanel", () => { it("shows self-payment warning when destination equals source address", async () => { vi.mocked(useSorokit).mockReturnValue({ address: "GCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", - isConnected: true, + isConnected: true, client: {}, } as unknown as ReturnType); render(); @@ -277,7 +278,7 @@ describe("TransactionPanel", () => { await screen.findByText("Transaction failed"); // Click "New Transaction" (Try Again) button - const newTxBtn = screen.getByRole("button", { name: "New Transaction" }); + const newTxBtn = screen.getByRole("button", { name: /New Transaction/i }); fireEvent.click(newTxBtn); // Verify form values are preserved (not cleared) @@ -302,7 +303,7 @@ describe("TransactionPanel", () => { it("populates the asset selector with the correct asset codes from context balances", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, balances, } as unknown as ReturnType); @@ -320,7 +321,7 @@ describe("TransactionPanel", () => { mockGetClient(mockSubmit); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, balances, } as unknown as ReturnType); @@ -342,7 +343,7 @@ describe("TransactionPanel", () => { await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/Transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ asset: "USDC" }), ); @@ -351,7 +352,7 @@ describe("TransactionPanel", () => { it("disables the asset selector when no balances are loaded", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, balances: [], } as unknown as ReturnType); @@ -395,7 +396,7 @@ describe("TransactionPanel", () => { mockGetClient(mockSubmit); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, network: { name: "testnet", passphrase: "x", rpcUrl: "x", horizonUrl: "x" }, } as unknown as ReturnType); @@ -406,7 +407,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/Transaction submitted/i); expect(screen.getByText("Successful")).toBeInTheDocument(); const link = screen.getByRole("link", { name: /view on stellar expert/i }); @@ -457,7 +458,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/Transaction submitted/i); expect(onSuccess).toHaveBeenCalledWith(txResult); expect(onError).not.toHaveBeenCalled(); @@ -512,7 +513,7 @@ describe("TransactionPanel", () => { fireEvent.change(screen.getByLabelText("Amount (XLM)"), { target: { value: "10" } }); await reviewAndConfirm(); - expect(await screen.findByText("Transaction submitted")).toBeInTheDocument(); + expect(await screen.findByText(/Transaction submitted/i)).toBeInTheDocument(); }); }); @@ -571,7 +572,7 @@ describe("TransactionPanel", () => { }); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); - await screen.findByText("Transaction submitted"); + await screen.findByText(/Transaction submitted/i); expect(mockSubmit).toHaveBeenCalledWith( expect.objectContaining({ destination: validDest, amount: "10" }), ); @@ -613,7 +614,7 @@ describe("TransactionPanel", () => { it("renders 'Send XLM' while XLM is the selected asset (multi-balance wallet)", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, balances, } as unknown as ReturnType); @@ -626,7 +627,7 @@ describe("TransactionPanel", () => { it("renders 'Send USDC' once the user switches the asset select to USDC", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, + isConnected: true, client: {}, balances, } as unknown as ReturnType); diff --git a/src/components/TransactionStatusTracker.test.tsx b/src/components/TransactionStatusTracker.test.tsx index 72febb3..6af7fdf 100644 --- a/src/components/TransactionStatusTracker.test.tsx +++ b/src/components/TransactionStatusTracker.test.tsx @@ -70,7 +70,7 @@ describe("TransactionStatusTracker", () => { await flushAsyncUpdates(); expect( - screen.getByText("Confirmed", { selector: "span" }), + screen.getByText(/Confirmed/i, { selector: "span" }), ).toBeInTheDocument(); expect( @@ -94,7 +94,7 @@ describe("TransactionStatusTracker", () => { await flushAsyncUpdates(); expect( - screen.getByText("Failed", { selector: "span" }), + screen.getByText(/Failed/i, { selector: "span" }), ).toBeInTheDocument(); await act(async () => { diff --git a/src/components/ValidatorCard.test.tsx b/src/components/ValidatorCard.test.tsx index a05c853..0e43afd 100644 --- a/src/components/ValidatorCard.test.tsx +++ b/src/components/ValidatorCard.test.tsx @@ -44,7 +44,7 @@ describe("ValidatorCard — rendering", () => { it("renders uptime metric", () => { render(); - expect(screen.getByText("99.9%")).toBeInTheDocument(); + expect(screen.getByText(/99\\.9%/i)).toBeInTheDocument(); }); it("renders delegator count", () => { diff --git a/src/components/ValidatorSearch.test.tsx b/src/components/ValidatorSearch.test.tsx index 74f252f..7192711 100644 --- a/src/components/ValidatorSearch.test.tsx +++ b/src/components/ValidatorSearch.test.tsx @@ -130,7 +130,8 @@ describe("ValidatorSearch — interactions", () => { }); it("calls onChange with undefined minApy when input is cleared", () => { - const { onChange, filter } = renderSearch(); + const filter = { ...createDefaultFilter(), minApy: 5 }; + const { onChange } = renderSearch({ filter }); fireEvent.change( screen.getByRole("spinbutton", { name: /minimum apy/i }), { target: { value: "" } }, diff --git a/src/components/payment-flow.test.tsx b/src/components/payment-flow.test.tsx index 7207fca..92ccec5 100644 --- a/src/components/payment-flow.test.tsx +++ b/src/components/payment-flow.test.tsx @@ -46,7 +46,7 @@ describe("TransactionPanel integration", () => { const destInput = screen.getByLabelText("Destination Address"); const amountInput = screen.getByLabelText("Amount (XLM)"); - const submitBtn = screen.getByRole("button", { name: "Send Payment" }); + const submitBtn = screen.getByRole("button", { name: /Send (Payment|XLM)/i }); fireEvent.change(destInput, { target: { diff --git a/src/screens/NetworkScreen.test.tsx b/src/screens/NetworkScreen.test.tsx index 5fd8b5a..f178ede 100644 --- a/src/screens/NetworkScreen.test.tsx +++ b/src/screens/NetworkScreen.test.tsx @@ -12,6 +12,7 @@ vi.mock("@/context/useSorokit", () => ({ vi.mock("@hugeicons/react", () => ({ HugeiconsIcon: "div", + Loading01Icon: "div", })); const TESTNET_NETWORK = { diff --git a/src/screens/TransactionsScreen.test.tsx b/src/screens/TransactionsScreen.test.tsx index 24afccb..a33529f 100644 --- a/src/screens/TransactionsScreen.test.tsx +++ b/src/screens/TransactionsScreen.test.tsx @@ -75,7 +75,7 @@ describe("TransactionsScreen", () => { it("renders TransactionPanel with its section title", () => { render(); - expect(screen.getAllByText("Send Payment")[0]).toBeInTheDocument(); + expect(screen.getAllByText(/Send (Payment|XLM)/i)[0]).toBeInTheDocument(); }); it("renders FeeEstimator above TransactionPanel in the DOM", () => { @@ -83,7 +83,7 @@ describe("TransactionsScreen", () => { const allHeadings = Array.from(container.querySelectorAll("h3")); const feeHeading = screen.getByText("Network Fee"); - const txHeading = screen.getAllByText("Send Payment").find( + const txHeading = screen.getAllByText(/Send (Payment|XLM)/i).find( (el) => el.tagName === "H3", ); @@ -103,7 +103,7 @@ describe("TransactionsScreen", () => { const allHeadings = Array.from(container.querySelectorAll("h3")); const feeHeading = screen.getByText("Network Fee"); - const panelHeading = screen.getAllByText("Send Payment").find( + const panelHeading = screen.getAllByText(/Send (Payment|XLM)/i).find( (el) => el.tagName === "H3", ); const timelineHeading = screen.getByText("Activity Timeline"); diff --git a/src/setupTests.ts b/src/setupTests.ts index c832844..0bb66f7 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -5,6 +5,7 @@ import { afterEach } from "vitest"; afterEach(() => { cleanup(); + vi.useRealTimers(); }); // Node's experimental global Web Storage API (stable default as of Node 22+) @@ -72,3 +73,8 @@ if (!Element.prototype.hasPointerCapture) { Element.prototype.setPointerCapture = () => {}; Element.prototype.releasePointerCapture = () => {}; } + +vi.mock('@hugeicons/react', async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, Loading01Icon: actual.Loading01Icon || (() => null) }; +}); diff --git a/vitest.config.ts b/vitest.config.ts index c5ef3d8..d805a8a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -14,6 +14,7 @@ export default defineConfig({ globals: true, environment: "jsdom", setupFiles: ["./src/setupTests.ts"], + testTimeout: 10000, fileParallelism: false, pool: "forks", poolOptions: { From d52902a5ffd1e391d68061418f3fd11d985ac03e Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Sat, 29 Aug 2026 14:30:50 +0100 Subject: [PATCH 11/15] Fix unused variables from upstream merge --- src/components/BalanceList.test.tsx | 8 +------- src/components/TransactionHistory.tsx | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/BalanceList.test.tsx b/src/components/BalanceList.test.tsx index 125b21a..6c9fa45 100644 --- a/src/components/BalanceList.test.tsx +++ b/src/components/BalanceList.test.tsx @@ -56,13 +56,7 @@ const mockUsdcBalance = { assetCode: "USDC", assetIssuer: "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", }; -const mockUsdcBalance2 = { - asset: "USDC", - balance: "30.0000000", - assetType: "credit_alphanum4" as const, - assetCode: "USDC", - assetIssuer: "GB6USDTISSUERABCDEFGHIJKLMNOPQRSTUVWXYZ12345", -}; + const mockLpBalance = { asset: "LP-POOL-1", balance: "10.0000000", diff --git a/src/components/TransactionHistory.tsx b/src/components/TransactionHistory.tsx index 31c644a..70c89e2 100644 --- a/src/components/TransactionHistory.tsx +++ b/src/components/TransactionHistory.tsx @@ -5,7 +5,7 @@ import { CheckmarkCircle01Icon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; -import { memo, useEffect, useMemo, useRef, useState } from "react"; +import { memo, useEffect, useMemo, useState } from "react"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; From b661c849c9ea384ece4636d42c656d906e72005a Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Sat, 29 Aug 2026 14:34:20 +0100 Subject: [PATCH 12/15] Fix typescript error in TransactionPanel buildPreview --- src/components/TransactionPanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TransactionPanel.tsx b/src/components/TransactionPanel.tsx index a99983b..b3590b8 100644 --- a/src/components/TransactionPanel.tsx +++ b/src/components/TransactionPanel.tsx @@ -183,6 +183,7 @@ export function TransactionPanel({ /** Builds a preview of the transaction and opens the confirmation modal. */ async function buildPreview() { + if (!address) return; setIsBuildingPreview(true); try { const { data: feeData } = client ? await client.transaction.estimateFee() : { data: null }; From fd402c3b1b3afbf645aeda096ee79587665a0451 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Sat, 29 Aug 2026 15:01:38 +0100 Subject: [PATCH 13/15] Fix client mocking issues across tests --- src/components/AllowanceManager.test.tsx | 5 +---- src/components/NFTGallery.test.tsx | 2 +- src/components/PortfolioRebalancer.test.tsx | 5 +---- src/components/RewardHistory.test.tsx | 2 +- src/components/SorobanInvokeButton.test.tsx | 4 +--- src/components/SorobanPanel.test.tsx | 1 + src/components/TransactionHistory.test.tsx | 15 +++------------ .../TransactionHistoryTable.test.tsx | 2 +- src/components/TransactionPanel.test.tsx | 18 +++++++++--------- .../TransactionStatusTracker.test.tsx | 2 +- 10 files changed, 20 insertions(+), 36 deletions(-) diff --git a/src/components/AllowanceManager.test.tsx b/src/components/AllowanceManager.test.tsx index 4a2209f..20dc861 100644 --- a/src/components/AllowanceManager.test.tsx +++ b/src/components/AllowanceManager.test.tsx @@ -71,10 +71,7 @@ describe("AllowanceManager", () => { beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers({ shouldAdvanceTime: true }); - vi.mocked(useSorokit).mockReturnValue({ - address: ADDRESS, - isConnected: true, - } as unknown as ReturnType); + vi.mocked(useSorokit).mockReturnValue({ address: ADDRESS, isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); }); afterEach(() => { diff --git a/src/components/NFTGallery.test.tsx b/src/components/NFTGallery.test.tsx index e364188..b077f15 100644 --- a/src/components/NFTGallery.test.tsx +++ b/src/components/NFTGallery.test.tsx @@ -52,7 +52,7 @@ function makeNft(overrides: Partial = {}): Nft { function makeConnectedContext(extra = {}) { return { address: VALID_ADDRESS, - isConnected: true, + isConnected: true, get client() { return getClient(); }, ...extra, } as unknown as ReturnType; } diff --git a/src/components/PortfolioRebalancer.test.tsx b/src/components/PortfolioRebalancer.test.tsx index 9647391..62adca6 100644 --- a/src/components/PortfolioRebalancer.test.tsx +++ b/src/components/PortfolioRebalancer.test.tsx @@ -35,10 +35,7 @@ const FIVE_BALANCES = [ ]; function mockSorokit(overrides: Partial> = {}) { - vi.mocked(useSorokit).mockReturnValue({ - address: MOCK_ADDRESS, - isConnected: true, - isConnecting: false, + vi.mocked(useSorokit).mockReturnValue({ address: MOCK_ADDRESS, isConnected: true, get client() { return getClient(); }, isConnecting: false, isLoading: false, isLoadingAccount: false, balances: TWO_BALANCES, diff --git a/src/components/RewardHistory.test.tsx b/src/components/RewardHistory.test.tsx index f662b32..9700ac8 100644 --- a/src/components/RewardHistory.test.tsx +++ b/src/components/RewardHistory.test.tsx @@ -49,7 +49,7 @@ describe("RewardHistory — rendering", () => { />, ); // Total should be a positive number with XLM suffix - expect(screen.getByText(/XLM/)).toBeInTheDocument(); + expect(screen.getAllByText(/XLM/)[0]).toBeInTheDocument(); }); it("renders the event table header columns", () => { diff --git a/src/components/SorobanInvokeButton.test.tsx b/src/components/SorobanInvokeButton.test.tsx index 4734422..0de23fb 100644 --- a/src/components/SorobanInvokeButton.test.tsx +++ b/src/components/SorobanInvokeButton.test.tsx @@ -32,9 +32,7 @@ function mockInvokeContract(result: { data: unknown; error: string | null; statu describe("SorobanInvokeButton", () => { beforeEach(() => { vi.clearAllMocks(); - vi.mocked(useSorokit).mockReturnValue({ - isConnected: true, - } as unknown as ReturnType); + vi.mocked(useSorokit).mockReturnValue({ isConnected: true, get client() { return getClient(); } } as unknown as ReturnType); }); it("renders the method name as the button label by default", () => { diff --git a/src/components/SorobanPanel.test.tsx b/src/components/SorobanPanel.test.tsx index 4bb677a..f61cced 100644 --- a/src/components/SorobanPanel.test.tsx +++ b/src/components/SorobanPanel.test.tsx @@ -30,6 +30,7 @@ describe("SorobanPanel", () => { vi.mocked(useSorokit).mockReturnValue({ isConnected: true, address: "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA", + get client() { return getClient(); }, } as unknown as ReturnType); }); diff --git a/src/components/TransactionHistory.test.tsx b/src/components/TransactionHistory.test.tsx index 070bc0a..e01cf9b 100644 --- a/src/components/TransactionHistory.test.tsx +++ b/src/components/TransactionHistory.test.tsx @@ -209,10 +209,7 @@ describe("TransactionHistory", () => { }); // Switch wallet address to account B - vi.mocked(useSorokit).mockReturnValue({ - address: ADDRESS_B, - isConnected: true, - } as unknown as ReturnType); + vi.mocked(useSorokit).mockReturnValue({ address: ADDRESS_B, isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); rerender(); act(() => { vi.advanceTimersByTime(0); }); @@ -253,10 +250,7 @@ describe("TransactionHistory", () => { await waitFor(() => screen.getByText(/25 transactions/i)); // Switch wallet address to account B (fetch remains pending) - vi.mocked(useSorokit).mockReturnValue({ - address: ADDRESS_B, - isConnected: true, - } as unknown as ReturnType); + vi.mocked(useSorokit).mockReturnValue({ address: ADDRESS_B, isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); rerender(); act(() => { vi.advanceTimersByTime(0); }); @@ -778,10 +772,7 @@ describe("TransactionHistory", () => { // Change the address via the mocked hook const NEW_ADDRESS = "GNEWADDRESS12345678901234567890123456789012345678901234"; - vi.mocked(useSorokit).mockReturnValue({ - address: NEW_ADDRESS, - isConnected: true, - client: mockClient, + vi.mocked(useSorokit).mockReturnValue({ address: NEW_ADDRESS, isConnected: true, get client() { return getClient(); }, client: mockClient, } as unknown as ReturnType); rerender(); diff --git a/src/components/TransactionHistoryTable.test.tsx b/src/components/TransactionHistoryTable.test.tsx index 9df6610..f0fb644 100644 --- a/src/components/TransactionHistoryTable.test.tsx +++ b/src/components/TransactionHistoryTable.test.tsx @@ -8,7 +8,7 @@ import { TransactionHistoryTable } from "./TransactionHistoryTable"; // Mock context vi.mock("@/context/useSorokit", () => ({ - useSorokit: () => ({ address: "GABC123...", isConnected: true }), + useSorokit: () => ({ address: "GABC123...", isConnected: true, get client() { return getClient(); } }), })); // Mock client diff --git a/src/components/TransactionPanel.test.tsx b/src/components/TransactionPanel.test.tsx index 38d1f8f..b2c4bd5 100644 --- a/src/components/TransactionPanel.test.tsx +++ b/src/components/TransactionPanel.test.tsx @@ -47,7 +47,7 @@ describe("TransactionPanel", () => { vi.clearAllMocks(); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); }); @@ -176,7 +176,7 @@ describe("TransactionPanel", () => { it("shows error if address is null at submit time", async () => { vi.mocked(useSorokit).mockReturnValue({ address: null, - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); render(); @@ -202,7 +202,7 @@ describe("TransactionPanel", () => { it("shows self-payment warning when destination equals source address", async () => { vi.mocked(useSorokit).mockReturnValue({ address: "GCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, } as unknown as ReturnType); render(); @@ -302,7 +302,7 @@ describe("TransactionPanel", () => { it("populates the asset selector with the correct asset codes from context balances", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, balances, } as unknown as ReturnType); @@ -320,7 +320,7 @@ describe("TransactionPanel", () => { mockGetClient(mockSubmit); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, balances, } as unknown as ReturnType); @@ -351,7 +351,7 @@ describe("TransactionPanel", () => { it("disables the asset selector when no balances are loaded", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, balances: [], } as unknown as ReturnType); @@ -395,7 +395,7 @@ describe("TransactionPanel", () => { mockGetClient(mockSubmit); vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, network: { name: "testnet", passphrase: "x", rpcUrl: "x", horizonUrl: "x" }, } as unknown as ReturnType); @@ -613,7 +613,7 @@ describe("TransactionPanel", () => { it("renders 'Send XLM' while XLM is the selected asset (multi-balance wallet)", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, balances, } as unknown as ReturnType); @@ -626,7 +626,7 @@ describe("TransactionPanel", () => { it("renders 'Send USDC' once the user switches the asset select to USDC", () => { vi.mocked(useSorokit).mockReturnValue({ address: "GABC", - isConnected: true, client: {}, + isConnected: true, get client() { return getClient(); }, balances, } as unknown as ReturnType); diff --git a/src/components/TransactionStatusTracker.test.tsx b/src/components/TransactionStatusTracker.test.tsx index 6af7fdf..957e3ce 100644 --- a/src/components/TransactionStatusTracker.test.tsx +++ b/src/components/TransactionStatusTracker.test.tsx @@ -33,7 +33,7 @@ describe("TransactionStatusTracker", () => { vi.useFakeTimers(); mockUseSorokit.mockReturnValue({ network: { name: "testnet", rpcUrl: "", horizonUrl: "", passphrase: "" }, - } as ReturnType); + , get client() { return getClient(); }} as unknown as ReturnType); Object.defineProperty(navigator, "clipboard", { value: { writeText: vi.fn().mockResolvedValue(undefined) }, configurable: true, From a6a0f7a6580bfb8b480f99b00f901449dee1b202 Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Sat, 29 Aug 2026 15:06:09 +0100 Subject: [PATCH 14/15] Fix TransactionStatusTracker.test.tsx syntax error --- src/components/TransactionStatusTracker.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/TransactionStatusTracker.test.tsx b/src/components/TransactionStatusTracker.test.tsx index 957e3ce..52110ac 100644 --- a/src/components/TransactionStatusTracker.test.tsx +++ b/src/components/TransactionStatusTracker.test.tsx @@ -33,7 +33,8 @@ describe("TransactionStatusTracker", () => { vi.useFakeTimers(); mockUseSorokit.mockReturnValue({ network: { name: "testnet", rpcUrl: "", horizonUrl: "", passphrase: "" }, - , get client() { return getClient(); }} as unknown as ReturnType); + get client() { return getClient(); } + } as unknown as ReturnType); Object.defineProperty(navigator, "clipboard", { value: { writeText: vi.fn().mockResolvedValue(undefined) }, configurable: true, From cf82d48534c9a685a2188d6c86c807bf68a9c2ae Mon Sep 17 00:00:00 2001 From: Trovic1 Date: Sat, 29 Aug 2026 15:26:40 +0100 Subject: [PATCH 15/15] Fix UI test matchers for upstream layout changes --- src/components/NFTGallery.test.tsx | 24 ++++++++++++------------ src/components/SorobanPanel.test.tsx | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/components/NFTGallery.test.tsx b/src/components/NFTGallery.test.tsx index b077f15..5bf077d 100644 --- a/src/components/NFTGallery.test.tsx +++ b/src/components/NFTGallery.test.tsx @@ -147,7 +147,7 @@ describe("NFTCard", () => { onList={onList} /> ); - expect(screen.getByText("Blue")).toBeInTheDocument(); + expect(screen.getAllByText("Blue")[0]).toBeInTheDocument(); expect(screen.getByText("10%")).toBeInTheDocument(); expect(screen.getByText("Laser")).toBeInTheDocument(); expect(screen.getByText("2%")).toBeInTheDocument(); @@ -277,7 +277,7 @@ describe("NFTCard", () => { selected={false} bulkMode={false} onSelect={onSelect} onSend={onSend} onList={onList} /> ); - expect(screen.getByText("Rare")).toBeInTheDocument(); + expect(screen.getAllByText("Rare")[0]).toBeInTheDocument(); }); it("labels as Common when no rank info", () => { @@ -792,8 +792,8 @@ describe("NFTGallery — List for Sale dialog", () => { it("opens List dialog showing NFT name and floor price", async () => { await openListDialog(); - expect(screen.getByText(/cool cat #1/i)).toBeInTheDocument(); - expect(screen.getByText(/100 xlm/i)).toBeInTheDocument(); + expect(screen.getAllByText(/cool cat #1/i)[0]).toBeInTheDocument(); + expect(screen.getAllByText(/100 xlm/i)[0]).toBeInTheDocument(); }); it("shows validation error when price is empty", async () => { @@ -886,7 +886,7 @@ describe("NFTGallery — NFT Detail dialog", () => { await waitFor(() => screen.getAllByTestId("nft-card")); fireEvent.click(screen.getByRole("listitem")); await waitFor(() => { - expect(screen.getByRole("dialog", { name: /nft detail: detail cat/i })).toBeInTheDocument(); + expect(screen.getByRole("dialog", { name: /Detail Cat/i })).toBeInTheDocument(); }); }); @@ -894,18 +894,18 @@ describe("NFTGallery — NFT Detail dialog", () => { render(); await waitFor(() => screen.getAllByTestId("nft-card")); fireEvent.click(screen.getByRole("listitem")); - await waitFor(() => screen.getByRole("dialog", { name: /nft detail/i })); + await waitFor(() => screen.getByRole("dialog", { name: /Detail Cat/i })); expect(screen.getByText("A detailed NFT")).toBeInTheDocument(); - expect(screen.getByText("300 XLM")).toBeInTheDocument(); - expect(screen.getByText("#10 / 500")).toBeInTheDocument(); - expect(screen.getByText("350 XLM")).toBeInTheDocument(); + expect(screen.getAllByText("300 XLM")[0]).toBeInTheDocument(); + expect(screen.getAllByText("#10 / 500")[0]).toBeInTheDocument(); + expect(screen.getAllByText("350 XLM")[0]).toBeInTheDocument(); }); it("shows all trait types and rarities in detail", async () => { render(); await waitFor(() => screen.getAllByTestId("nft-card")); fireEvent.click(screen.getByRole("listitem")); - await waitFor(() => screen.getByRole("dialog", { name: /nft detail/i })); + await waitFor(() => screen.getByRole("dialog", { name: /Detail Cat/i })); expect(screen.getByText("Eyes")).toBeInTheDocument(); expect(screen.getByText("5.0% have this")).toBeInTheDocument(); expect(screen.getByText("Fur")).toBeInTheDocument(); @@ -916,10 +916,10 @@ describe("NFTGallery — NFT Detail dialog", () => { render(); await waitFor(() => screen.getAllByTestId("nft-card")); fireEvent.click(screen.getByRole("listitem")); - await waitFor(() => screen.getByRole("dialog", { name: /nft detail/i })); + await waitFor(() => screen.getByRole("dialog", { name: /Detail Cat/i })); fireEvent.click(screen.getByRole("button", { name: /^close$/i })); await waitFor(() => { - expect(screen.queryByRole("dialog", { name: /nft detail/i })).not.toBeInTheDocument(); + expect(screen.queryByRole("dialog", { name: /Detail Cat/i })).not.toBeInTheDocument(); }); }); }); diff --git a/src/components/SorobanPanel.test.tsx b/src/components/SorobanPanel.test.tsx index f61cced..1eb2a45 100644 --- a/src/components/SorobanPanel.test.tsx +++ b/src/components/SorobanPanel.test.tsx @@ -2,6 +2,7 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest"; import { useSorokit } from "@/context/useSorokit"; +import { getClient } from "@/lib/client"; import { SorobanPanel } from "./SorobanPanel";