Skip to content

Commit

Permalink
test: Add wallet connection hook tests (#5689)
Browse files Browse the repository at this point in the history
  • Loading branch information
joaquim-verges authored Dec 13, 2024
1 parent 29d32b8 commit 77c8d0e
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<ConnectionManagerCtx.Provider value={manager}>
{children}
</ConnectionManagerCtx.Provider>
);
};

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 <ThirdwebProvider>");
});
});
105 changes: 105 additions & 0 deletions packages/thirdweb/src/react/core/hooks/wallets/useConnect.test.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<ConnectionManagerCtx.Provider value={manager}>
{children}
</ConnectionManagerCtx.Provider>
);
};

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 <ThirdwebProvider>");
});
});
Original file line number Diff line number Diff line change
@@ -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 (
<ConnectionManagerCtx.Provider value={manager}>
{children}
</ConnectionManagerCtx.Provider>
);
};

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 <ThirdwebProvider>");
});
});
25 changes: 21 additions & 4 deletions packages/thirdweb/test/src/mocks/storage.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
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(
`https://${getThirdwebDomains().storage}/ipfs/upload`,
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 {
Expand All @@ -20,7 +21,7 @@ export const handlers = [
files: fileNames.reduce((acc, fileName) => {
acc[fileName] = { cid: mockIpfsHash };
return acc;
}, {})
}, {}),
});
}
},
Expand All @@ -40,3 +41,19 @@ export const handlers = [
});
}),
];

export class MockStorage implements AsyncStorage {
private storage: Map<string, string> = new Map();

getItem(key: string): Promise<string | null> {
return Promise.resolve(this.storage.get(key) ?? null);
}
setItem(key: string, value: string): Promise<void> {
this.storage.set(key, value);
return Promise.resolve();
}
removeItem(key: string): Promise<void> {
this.storage.delete(key);
return Promise.resolve();
}
}

0 comments on commit 77c8d0e

Please sign in to comment.