diff --git a/packages/thirdweb/src/react/core/hooks/wallets/useAddConnectedWallet.test.tsx b/packages/thirdweb/src/react/core/hooks/wallets/useAddConnectedWallet.test.tsx
new file mode 100644
index 00000000000..7b439d2c402
--- /dev/null
+++ b/packages/thirdweb/src/react/core/hooks/wallets/useAddConnectedWallet.test.tsx
@@ -0,0 +1,52 @@
+import { renderHook } from "@testing-library/react";
+import type { ReactNode } from "react";
+import { describe, expect, it } from "vitest";
+import { MockStorage } from "../../../../../test/src/mocks/storage.js";
+import { TEST_CLIENT } from "../../../../../test/src/test-clients.js";
+import { TEST_ACCOUNT_A } from "../../../../../test/src/test-wallets.js";
+import { createWalletAdapter } from "../../../../adapters/wallet-adapter.js";
+import { ethereum } from "../../../../chains/chain-definitions/ethereum.js";
+import { createConnectionManager } from "../../../../wallets/manager/index.js";
+import { ConnectionManagerCtx } from "../../providers/connection-manager.js";
+import { useAddConnectedWallet } from "./useAddConnectedWallet.js";
+
+describe("useAddConnectedWallet", () => {
+ // Mock the connection manager
+ const mockStorage = new MockStorage();
+ const manager = createConnectionManager(mockStorage);
+
+ // Create a wrapper component with the mocked context
+ const wrapper = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+ };
+
+ const wallet = createWalletAdapter({
+ adaptedAccount: TEST_ACCOUNT_A,
+ client: TEST_CLIENT,
+ chain: ethereum,
+ onDisconnect: () => {},
+ switchChain: () => {},
+ });
+
+ it("should add a wallet to the connection manager", async () => {
+ // Render the hook
+ const { result } = renderHook(() => useAddConnectedWallet(), { wrapper });
+ result.current(wallet);
+
+ expect(manager.connectedWallets.getValue()).toHaveLength(1);
+ expect(manager.connectedWallets.getValue()[0]).toEqual(wallet);
+ // add connected wallet should not set the active wallet
+ expect(manager.activeWalletStore.getValue()).toBeUndefined();
+ });
+
+ it("should throw an error when used outside of ThirdwebProvider", () => {
+ // Render the hook without a provider
+ expect(() => {
+ renderHook(() => useAddConnectedWallet());
+ }).toThrow("useAddConnectedWallet must be used within ");
+ });
+});
diff --git a/packages/thirdweb/src/react/core/hooks/wallets/useConnect.test.tsx b/packages/thirdweb/src/react/core/hooks/wallets/useConnect.test.tsx
new file mode 100644
index 00000000000..eac4407c0ee
--- /dev/null
+++ b/packages/thirdweb/src/react/core/hooks/wallets/useConnect.test.tsx
@@ -0,0 +1,105 @@
+import { renderHook } from "@testing-library/react";
+import type { ReactNode } from "react";
+import { beforeEach, describe, expect, it } from "vitest";
+import { MockStorage } from "../../../../../test/src/mocks/storage.js";
+import { TEST_CLIENT } from "../../../../../test/src/test-clients.js";
+import { TEST_ACCOUNT_A } from "../../../../../test/src/test-wallets.js";
+import { createWalletAdapter } from "../../../../adapters/wallet-adapter.js";
+import { ethereum } from "../../../../chains/chain-definitions/ethereum.js";
+import {
+ type ConnectionManager,
+ createConnectionManager,
+} from "../../../../wallets/manager/index.js";
+import { ConnectionManagerCtx } from "../../providers/connection-manager.js";
+import { useActiveWalletConnectionStatus } from "./useActiveWalletConnectionStatus.js";
+import { useConnect } from "./useConnect.js";
+
+describe("useAddConnectedWallet", () => {
+ // Mock the connection manager
+ const mockStorage = new MockStorage();
+ let manager: ConnectionManager;
+
+ // Create a wrapper component with the mocked context
+ const wrapper = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+ };
+
+ const wallet = createWalletAdapter({
+ adaptedAccount: TEST_ACCOUNT_A,
+ client: TEST_CLIENT,
+ chain: ethereum,
+ onDisconnect: () => {},
+ switchChain: () => {},
+ });
+
+ beforeEach(() => {
+ manager = createConnectionManager(mockStorage);
+ });
+
+ it("should connect a wallet to the connection manager", async () => {
+ const { result: statusResult } = renderHook(
+ () => useActiveWalletConnectionStatus(),
+ {
+ wrapper,
+ },
+ );
+ const { result } = renderHook(() => useConnect(), { wrapper });
+ expect(statusResult.current).toEqual("disconnected");
+ await result.current.connect(async () => wallet);
+ expect(statusResult.current).toEqual("connected");
+
+ // should add to connected wallets
+ expect(manager.connectedWallets.getValue()).toHaveLength(1);
+ expect(manager.connectedWallets.getValue()[0]).toEqual(wallet);
+ // should set the active wallet
+ expect(manager.activeWalletStore.getValue()).toEqual(wallet);
+ });
+
+ it("should handle a function that returns a wallet", async () => {
+ const { result: statusResult } = renderHook(
+ () => useActiveWalletConnectionStatus(),
+ {
+ wrapper,
+ },
+ );
+ const { result } = renderHook(() => useConnect(), { wrapper });
+ expect(statusResult.current).toEqual("disconnected");
+ await result.current.connect(async () => wallet);
+ expect(statusResult.current).toEqual("connected");
+
+ // should add to connected wallets
+ expect(manager.connectedWallets.getValue()).toHaveLength(1);
+ expect(manager.connectedWallets.getValue()[0]).toEqual(wallet);
+ // should set the active wallet
+ expect(manager.activeWalletStore.getValue()).toEqual(wallet);
+ });
+
+ it("should handle an error when connecting a wallet", async () => {
+ const { result: statusResult } = renderHook(
+ () => useActiveWalletConnectionStatus(),
+ {
+ wrapper,
+ },
+ );
+ expect(statusResult.current).toEqual("disconnected");
+ const { result } = renderHook(() => useConnect(), { wrapper });
+ await result.current.connect(async () => {
+ throw new Error("test");
+ });
+
+ expect(statusResult.current).toEqual("disconnected");
+ // should set the active wallet
+ expect(manager.activeWalletStore.getValue()).toEqual(undefined);
+ });
+
+ it("should throw an error when used outside of ThirdwebProvider", () => {
+ // Render the hook without a provider
+ expect(() => {
+ renderHook(() => useConnect());
+ }).toThrow("useConnect must be used within ");
+ });
+});
diff --git a/packages/thirdweb/src/react/core/hooks/wallets/useSetActiveWallet.test.tsx b/packages/thirdweb/src/react/core/hooks/wallets/useSetActiveWallet.test.tsx
new file mode 100644
index 00000000000..bef745a8d2a
--- /dev/null
+++ b/packages/thirdweb/src/react/core/hooks/wallets/useSetActiveWallet.test.tsx
@@ -0,0 +1,53 @@
+import { renderHook } from "@testing-library/react";
+import type { ReactNode } from "react";
+import { describe, expect, it } from "vitest";
+import { MockStorage } from "../../../../../test/src/mocks/storage.js";
+import { TEST_CLIENT } from "../../../../../test/src/test-clients.js";
+import { TEST_ACCOUNT_A } from "../../../../../test/src/test-wallets.js";
+import { createWalletAdapter } from "../../../../adapters/wallet-adapter.js";
+import { ethereum } from "../../../../chains/chain-definitions/ethereum.js";
+import { createConnectionManager } from "../../../../wallets/manager/index.js";
+import { ConnectionManagerCtx } from "../../providers/connection-manager.js";
+import { useSetActiveWallet } from "./useSetActiveWallet.js";
+
+describe("useAddConnectedWallet", () => {
+ // Mock the connection manager
+ const mockStorage = new MockStorage();
+ const manager = createConnectionManager(mockStorage);
+
+ // Create a wrapper component with the mocked context
+ const wrapper = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+ };
+
+ const wallet = createWalletAdapter({
+ adaptedAccount: TEST_ACCOUNT_A,
+ client: TEST_CLIENT,
+ chain: ethereum,
+ onDisconnect: () => {},
+ switchChain: () => {},
+ });
+
+ it("should add a wallet to the connection manager", async () => {
+ // Render the hook
+ const { result } = renderHook(() => useSetActiveWallet(), { wrapper });
+ result.current(wallet);
+
+ // should add to connected wallets
+ expect(manager.connectedWallets.getValue()).toHaveLength(1);
+ expect(manager.connectedWallets.getValue()[0]).toEqual(wallet);
+ // should set the active wallet
+ expect(manager.activeWalletStore.getValue()).toEqual(wallet);
+ });
+
+ it("should throw an error when used outside of ThirdwebProvider", () => {
+ // Render the hook without a provider
+ expect(() => {
+ renderHook(() => useSetActiveWallet());
+ }).toThrow("useSetActiveWallet must be used within ");
+ });
+});
diff --git a/packages/thirdweb/test/src/mocks/storage.ts b/packages/thirdweb/test/src/mocks/storage.ts
index eecf647b761..35b1ebec24c 100644
--- a/packages/thirdweb/test/src/mocks/storage.ts
+++ b/packages/thirdweb/test/src/mocks/storage.ts
@@ -1,5 +1,6 @@
import { http, HttpResponse } from "msw";
import { getThirdwebDomains } from "../../../src/utils/domains.js";
+import type { AsyncStorage } from "../../../src/utils/storage/AsyncStorage.js";
export const handlers = [
http.post(
@@ -7,11 +8,11 @@ export const handlers = [
async ({ request }) => {
console.log("MSW handler hit for IPFS upload");
const formData = await request.formData();
- const files = formData.getAll('file');
+ const files = formData.getAll("file");
const fileNames = files.map((file: any) => file.name);
-
+
const mockIpfsHash = "QmTest1234567890TestHash";
-
+
if (fileNames.length === 1 && fileNames[0] === "file.txt") {
return HttpResponse.json({ IpfsHash: mockIpfsHash });
} else {
@@ -20,7 +21,7 @@ export const handlers = [
files: fileNames.reduce((acc, fileName) => {
acc[fileName] = { cid: mockIpfsHash };
return acc;
- }, {})
+ }, {}),
});
}
},
@@ -40,3 +41,19 @@ export const handlers = [
});
}),
];
+
+export class MockStorage implements AsyncStorage {
+ private storage: Map = new Map();
+
+ getItem(key: string): Promise {
+ return Promise.resolve(this.storage.get(key) ?? null);
+ }
+ setItem(key: string, value: string): Promise {
+ this.storage.set(key, value);
+ return Promise.resolve();
+ }
+ removeItem(key: string): Promise {
+ this.storage.delete(key);
+ return Promise.resolve();
+ }
+}