Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The describe block title should be useConnect to match the hook under test. The current title useAddConnectedWallet appears to be a copy/paste error.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

// 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();
}
}
Loading