From 42ad5e0c1d07997c30e984b84abd7615de5378ee Mon Sep 17 00:00:00 2001 From: Ryjen1 Date: Thu, 2 Jul 2026 11:51:19 +0100 Subject: [PATCH] test(wallet): add NetworkMismatchBanner tests (#211) Verify that the network mismatch banner is displayed when the wallet and app networks mismatch, and hidden when they match. Test the specific warning copy for switching to Stellar Testnet or Mainnet, and assert that the banner can be dismissed via sessionStorage. --- .../components/NetworkMismatchBanner.test.tsx | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 apps/web/src/features/wallet/components/NetworkMismatchBanner.test.tsx diff --git a/apps/web/src/features/wallet/components/NetworkMismatchBanner.test.tsx b/apps/web/src/features/wallet/components/NetworkMismatchBanner.test.tsx new file mode 100644 index 0000000..c0bcdb0 --- /dev/null +++ b/apps/web/src/features/wallet/components/NetworkMismatchBanner.test.tsx @@ -0,0 +1,125 @@ +import { describe, it, expect, beforeEach, afterEach, beforeAll, vi } from "vitest" +import { render, screen, fireEvent } from "@testing-library/react" +import { useWalletStore } from "../store/wallet-store" +import { NetworkMismatchBanner } from "./NetworkMismatchBanner" +import { NETWORK } from "@/app/config/network" + +// ── Mock router ────────────────────────────────────────────────────────────── +const mockLocation = { pathname: "/faucet" } + +vi.mock("@tanstack/react-router", () => ({ + useLocation: () => mockLocation, +})) + +describe("NetworkMismatchBanner (#211)", () => { + let originalNetworkName: "testnet" | "mainnet" + + beforeAll(() => { + originalNetworkName = NETWORK.name + }) + + beforeEach(() => { + // Clear sessionStorage and reset mock defaults + sessionStorage.clear() + mockLocation.pathname = "/faucet" + NETWORK.name = "testnet" + + // Default wallet state: connected to testnet + useWalletStore.setState({ + address: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + walletId: "freighter", + status: "connected", + pendingTransactionXdr: null, + network: "testnet", + }) + }) + + afterEach(() => { + vi.restoreAllMocks() + NETWORK.name = originalNetworkName + }) + + it("does not render when networks match (both testnet)", () => { + render() + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + }) + + it("does not render when wallet is disconnected", () => { + useWalletStore.setState({ + address: null, + walletId: null, + status: "disconnected", + network: "testnet", + }) + render() + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + }) + + it("does not render on landing page (pathname = '/') even with mismatch", () => { + mockLocation.pathname = "/" + NETWORK.name = "mainnet" // Mismatch: app mainnet vs wallet testnet + render() + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + }) + + it("renders warning banner when app is testnet and wallet is mainnet", () => { + // Wallet mainnet, App testnet + useWalletStore.setState({ + address: "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + walletId: "freighter", + status: "connected", + network: "mainnet", + }) + + render() + + const alert = screen.getByRole("alert") + expect(alert).toBeInTheDocument() + expect( + screen.getByText( + /Your wallet is connected to Mainnet but this app is running on Testnet\. Please switch networks in your wallet\./i, + ), + ).toBeInTheDocument() + }) + + it("renders warning banner when app is mainnet and wallet is testnet", () => { + // Wallet testnet, App mainnet + NETWORK.name = "mainnet" + + render() + + const alert = screen.getByRole("alert") + expect(alert).toBeInTheDocument() + expect( + screen.getByText( + /Your wallet is connected to Testnet but this app is running on Mainnet\. Please switch networks in your wallet\./i, + ), + ).toBeInTheDocument() + }) + + it("dismisses the banner when the Dismiss button is clicked", () => { + NETWORK.name = "mainnet" // Wallet testnet vs App mainnet + + const { rerender } = render() + + expect(screen.getByRole("alert")).toBeInTheDocument() + + const dismissButton = screen.getByRole("button", { name: /Dismiss/i }) + fireEvent.click(dismissButton) + + // Alert should immediately disappear from DOM + rerender() + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + + // Assert that sessionStorage was updated + expect(sessionStorage.getItem("so4-network-mismatch-dismissed")).toBe("1") + }) + + it("respects pre-existing dismissal state from sessionStorage", () => { + NETWORK.name = "mainnet" // Wallet testnet vs App mainnet + sessionStorage.setItem("so4-network-mismatch-dismissed", "1") + + render() + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + }) +})