diff --git a/src/pages/WalletModal.tsx b/src/pages/WalletModal.tsx
index e7ce289..c129e84 100644
--- a/src/pages/WalletModal.tsx
+++ b/src/pages/WalletModal.tsx
@@ -33,6 +33,12 @@ import {
} from "@/components/connect-wallet-modal.messages";
import { useReducedMotion } from "@/hooks/useReducedMotion";
import { getKit } from "@/constants/wallet-kits.constant";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
// ─── Types ────────────────────────────────────────────────────────────────────
@@ -251,18 +257,27 @@ export function WalletModal({
{truncateAddress(walletAddress ?? "")}
-
+
+
+
+
+
+
+ {copied ? addressCopiedLabel : copyAddressLabel}
+
+
+
diff --git a/src/pages/__tests__/WalletModal.test.tsx b/src/pages/__tests__/WalletModal.test.tsx
index 2bc202d..4c2d25c 100644
--- a/src/pages/__tests__/WalletModal.test.tsx
+++ b/src/pages/__tests__/WalletModal.test.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { render, screen } from "@testing-library/react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { WalletModal } from "../WalletModal";
// ─── Mocks ────────────────────────────────────────────────────────────────────
@@ -13,23 +13,32 @@ jest.mock("next/image", () => ({
),
}));
+// Mock wallet-kits.constant to avoid ESM stellar-wallets-kit import issue
+jest.mock("@/constants/wallet-kits.constant", () => ({
+ getKit: jest.fn(() => ({
+ getSupportedWallets: jest.fn().mockResolvedValue([]),
+ })),
+}));
+
// Mock useReducedMotion hook
const mockUseReducedMotion = jest.fn(() => false);
jest.mock("@/hooks/useReducedMotion", () => ({
useReducedMotion: () => mockUseReducedMotion(),
}));
-// Mock useWallet hook
+// Mock useWallet hook — default to a connected wallet so the copy
+// address button (and its tooltip) is rendered.
+const mockWallet = {
+ connectWallet: jest.fn(),
+ disconnectWallet: jest.fn(),
+ isConnected: true,
+ walletAddress: "GDQERJZWU...AbCd1234",
+ walletName: "Freighter",
+ isConnecting: false,
+ error: null,
+};
jest.mock("@/hooks/useWallet.hook", () => ({
- useWallet: () => ({
- connectWallet: jest.fn(),
- disconnectWallet: jest.fn(),
- isConnected: false,
- walletAddress: null,
- walletName: null,
- isConnecting: false,
- error: null,
- }),
+ useWallet: () => mockWallet,
}));
// ─── Tests ────────────────────────────────────────────────────────────────────
@@ -63,7 +72,6 @@ describe("WalletModal — reduced-motion fallback (#633)", () => {
const dialogContent = screen.getByRole("dialog");
expect(dialogContent).toBeInTheDocument();
- // Verify animation classes are stripped/bypassed and replaced with static classes
expect(dialogContent.className).toContain("duration-0");
expect(dialogContent.className).toContain("transition-none");
expect(dialogContent.className).not.toContain("data-[state=open]:animate-in");
@@ -77,10 +85,40 @@ describe("WalletModal — reduced-motion fallback (#633)", () => {
const dialogContent = screen.getByRole("dialog");
expect(dialogContent).toBeInTheDocument();
- // Verify system preference is respected and static fallback is applied
expect(dialogContent.className).toContain("duration-0");
expect(dialogContent.className).toContain("transition-none");
expect(dialogContent.className).not.toContain("data-[state=open]:animate-in");
expect(dialogContent.className).not.toContain("duration-200");
});
});
+
+describe("WalletModal — copy-address tooltip (#783)", () => {
+ beforeEach(() => {
+ localStorage.clear();
+ mockUseReducedMotion.mockReset();
+ mockUseReducedMotion.mockReturnValue(false);
+ });
+
+ it("renders a copy button with a tooltip trigger when a wallet is connected", () => {
+ render();
+
+ // The copy address button is present when connected
+ const copyButton = screen.getByRole("button", { name: /copy wallet address/i });
+ expect(copyButton).toBeInTheDocument();
+ });
+
+ it("shows 'Copy wallet address' tooltip content when the tooltip is opened", async () => {
+ render();
+
+ const copyButton = screen.getByRole("button", { name: /copy wallet address/i });
+
+ // Focus the trigger (Radix tooltip opens on focus / hover)
+ fireEvent.focus(copyButton);
+
+ // The tooltip content should become visible
+ await waitFor(() => {
+ expect(screen.getByRole("tooltip")).toBeInTheDocument();
+ });
+ expect(screen.getByRole("tooltip")).toHaveTextContent("Copy wallet address");
+ });
+});
\ No newline at end of file