Skip to content

Commit 5fcbb56

Browse files
committed
more tests
1 parent 195e36f commit 5fcbb56

File tree

4 files changed

+326
-154
lines changed

4 files changed

+326
-154
lines changed

packages/thirdweb/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@
273273
"test:watch": "vitest -c ./test/vitest.config.ts dev",
274274
"test": "vitest run -c ./test/vitest.config.ts --coverage",
275275
"test:cov": "vitest dev -c ./test/vitest.config.ts --coverage",
276+
"test:ui": "vitest dev -c ./test/vitest.config.ts --coverage --ui",
276277
"test:dev": "vitest run -c ./test/vitest.config.ts",
277278
"test:react": "vitest run -c ./test/vitest.config.ts dev --ui src/react",
278279
"typedoc": "bun run scripts/typedoc.mjs",

packages/thirdweb/src/wallets/manager/connection-manager.test.ts

Lines changed: 177 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import type { ThirdwebClient } from "../../client/client.js";
66
import type { AsyncStorage } from "../../utils/storage/AsyncStorage.js";
77
import type { Account, Wallet } from "../interfaces/wallet.js";
88
import type { SmartWalletOptions } from "../smart/types.js";
9-
import { createConnectionManager } from "./index.js";
9+
import {
10+
createConnectionManager,
11+
handleSmartWalletConnection,
12+
} from "./index.js";
1013

1114
describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
1215
let storage: AsyncStorage;
@@ -26,7 +29,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
2629
wallet = {
2730
id: "wallet-id",
2831
getAccount: vi.fn().mockReturnValue(account),
29-
subscribe: vi.fn(),
32+
subscribe: vi.fn().mockReturnValue(vi.fn()),
3033
disconnect: vi.fn(),
3134
switchChain: vi.fn(),
3235
getChain: vi.fn().mockReturnValue(sepolia),
@@ -177,4 +180,176 @@ describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
177180
manager.removeConnectedWallet(wallet);
178181
expect(manager.connectedWallets.getValue()).not.toContain(wallet);
179182
});
183+
184+
it("should update active wallet chain to a defined chain", async () => {
185+
const manager = createConnectionManager(storage);
186+
187+
// Connect the wallet to initialize the active wallet chain
188+
await manager.handleConnection(wallet, { client });
189+
190+
// Define a new chain and update the definedChainsStore
191+
const newChain = {
192+
id: 11155111,
193+
name: "New Defined Chain",
194+
rpc: "https://rpc3.example.com",
195+
};
196+
manager.defineChains([newChain]);
197+
198+
// Simulate the effect that updates the active wallet chain
199+
const definedChain = manager.activeWalletChainStore.getValue();
200+
expect(definedChain).toEqual(newChain);
201+
});
202+
});
203+
204+
describe("handleSmartWalletConnection", () => {
205+
let client: ThirdwebClient;
206+
let eoaWallet: Wallet;
207+
let smartWalletOptions: SmartWalletOptions;
208+
209+
beforeEach(() => {
210+
client = TEST_CLIENT;
211+
eoaWallet = {
212+
id: "eoa-wallet-id",
213+
getAccount: vi.fn().mockReturnValue(TEST_ACCOUNT_A),
214+
subscribe: vi.fn().mockReturnValue(vi.fn()),
215+
disconnect: vi.fn(),
216+
switchChain: vi.fn(),
217+
getChain: vi.fn().mockReturnValue(sepolia),
218+
getConfig: vi.fn(),
219+
} as unknown as Wallet;
220+
smartWalletOptions = {
221+
chain: sepolia,
222+
} as SmartWalletOptions;
223+
});
224+
225+
it("should connect a smart wallet and subscribe to disconnect event", async () => {
226+
const onWalletDisconnect = vi.fn();
227+
const smartWallet = await handleSmartWalletConnection(
228+
eoaWallet,
229+
client,
230+
smartWalletOptions,
231+
onWalletDisconnect,
232+
);
233+
234+
expect(smartWallet.getAccount()).toBeTruthy();
235+
236+
expect(eoaWallet.subscribe).toHaveBeenCalledWith(
237+
"disconnect",
238+
expect.any(Function),
239+
);
240+
});
241+
242+
it("should call onWalletDisconnect when EOA wallet disconnects", async () => {
243+
const onWalletDisconnect = vi.fn();
244+
const smartWallet = await handleSmartWalletConnection(
245+
eoaWallet,
246+
client,
247+
smartWalletOptions,
248+
onWalletDisconnect,
249+
);
250+
251+
// biome-ignore lint/suspicious/noExplicitAny: Mocked function
252+
const disconnectCallback = (eoaWallet.subscribe as any).mock.calls[0][1];
253+
disconnectCallback();
254+
255+
expect(onWalletDisconnect).toHaveBeenCalledWith(smartWallet);
256+
});
257+
258+
it("should throw an error if EOA wallet has no account", async () => {
259+
eoaWallet.getAccount = vi.fn().mockReturnValue(null);
260+
261+
await expect(
262+
handleSmartWalletConnection(
263+
eoaWallet,
264+
client,
265+
smartWalletOptions,
266+
vi.fn(),
267+
),
268+
).rejects.toThrow("Cannot set a wallet without an account as active");
269+
});
270+
});
271+
272+
describe("Connection Manager Error Handling", () => {
273+
let storage: AsyncStorage;
274+
let client: ThirdwebClient;
275+
let wallet: Wallet;
276+
277+
beforeEach(() => {
278+
storage = {
279+
getItem: vi.fn(),
280+
setItem: vi.fn(),
281+
removeItem: vi.fn(),
282+
};
283+
client = TEST_CLIENT;
284+
wallet = {
285+
id: "wallet-id",
286+
getAccount: vi.fn().mockReturnValue(null), // Simulate wallet without an account
287+
subscribe: vi.fn(),
288+
disconnect: vi.fn(),
289+
switchChain: vi.fn(),
290+
getChain: vi.fn().mockReturnValue(sepolia),
291+
getConfig: vi.fn(),
292+
} as unknown as Wallet;
293+
});
294+
295+
it("should throw an error if trying to set a wallet without an account as active", async () => {
296+
const manager = createConnectionManager(storage);
297+
298+
await expect(manager.setActiveWallet(wallet)).rejects.toThrow(
299+
"Cannot set a wallet without an account as active",
300+
);
301+
});
302+
303+
it("should throw an error if handleConnection is called with a wallet without an account", async () => {
304+
const manager = createConnectionManager(storage);
305+
306+
await expect(manager.handleConnection(wallet, { client })).rejects.toThrow(
307+
"Cannot set a wallet without an account as active",
308+
);
309+
});
310+
});
311+
312+
describe("Connection Manager Event Subscriptions", () => {
313+
let storage: AsyncStorage;
314+
let client: ThirdwebClient;
315+
let wallet: Wallet;
316+
let account: Account;
317+
318+
beforeEach(() => {
319+
storage = {
320+
getItem: vi.fn(),
321+
setItem: vi.fn(),
322+
removeItem: vi.fn(),
323+
};
324+
client = TEST_CLIENT;
325+
account = TEST_ACCOUNT_A;
326+
wallet = {
327+
id: "wallet-id",
328+
getAccount: vi.fn().mockReturnValue(account),
329+
subscribe: vi.fn().mockReturnValue(vi.fn()),
330+
disconnect: vi.fn(),
331+
switchChain: vi.fn(),
332+
getChain: vi.fn().mockReturnValue(sepolia),
333+
getConfig: vi.fn(),
334+
} as unknown as Wallet;
335+
});
336+
337+
it("should subscribe to accountChanged, chainChanged, and disconnect events", async () => {
338+
const manager = createConnectionManager(storage);
339+
340+
await manager.handleConnection(wallet, { client });
341+
342+
expect(wallet.subscribe).toHaveBeenCalledWith(
343+
"accountChanged",
344+
expect.any(Function),
345+
);
346+
expect(wallet.subscribe).toHaveBeenCalledWith(
347+
"chainChanged",
348+
expect.any(Function),
349+
);
350+
expect(wallet.subscribe).toHaveBeenCalledWith(
351+
"disconnect",
352+
expect.any(Function),
353+
);
354+
});
180355
});

packages/thirdweb/src/wallets/manager/index.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export function createConnectionManager(storage: AsyncStorage) {
134134
wallet,
135135
options.client,
136136
options.accountAbstraction,
137+
onWalletDisconnect,
137138
);
138139
} else {
139140
return wallet;
@@ -156,36 +157,6 @@ export function createConnectionManager(storage: AsyncStorage) {
156157
return activeWallet;
157158
};
158159

159-
const handleSmartWalletConnection = async (
160-
eoaWallet: Wallet,
161-
client: ThirdwebClient,
162-
options: SmartWalletOptions,
163-
) => {
164-
const signer = eoaWallet.getAccount();
165-
if (!signer) {
166-
throw new Error("Cannot set a wallet without an account as active");
167-
}
168-
169-
const wallet = smartWallet(options);
170-
171-
await wallet.connect({
172-
personalAccount: signer,
173-
client: client,
174-
chain: options.chain,
175-
});
176-
177-
// Disconnect the active wallet when the EOA disconnects if it the active wallet is a smart wallet
178-
const disconnectUnsub = eoaWallet.subscribe("disconnect", () => {
179-
handleDisconnect();
180-
});
181-
const handleDisconnect = () => {
182-
disconnectUnsub();
183-
onWalletDisconnect(wallet);
184-
};
185-
186-
return wallet;
187-
};
188-
189160
const connect = async (wallet: Wallet, options?: ConnectManagerOptions) => {
190161
// connectedWallet can be either wallet or smartWallet
191162
const connectedWallet = await handleConnection(wallet, options);
@@ -227,6 +198,7 @@ export function createConnectionManager(storage: AsyncStorage) {
227198
});
228199

229200
const handleDisconnect = () => {
201+
console.log("disconnecting");
230202
onWalletDisconnect(activeWallet);
231203
unsubAccounts();
232204
unsubChainChanged();
@@ -389,3 +361,37 @@ export async function getLastConnectedChain(
389361

390362
return null;
391363
}
364+
365+
/**
366+
* @internal
367+
*/
368+
export const handleSmartWalletConnection = async (
369+
eoaWallet: Wallet,
370+
client: ThirdwebClient,
371+
options: SmartWalletOptions,
372+
onWalletDisconnect: (wallet: Wallet) => void,
373+
) => {
374+
const signer = eoaWallet.getAccount();
375+
if (!signer) {
376+
throw new Error("Cannot set a wallet without an account as active");
377+
}
378+
379+
const wallet = smartWallet(options);
380+
381+
await wallet.connect({
382+
personalAccount: signer,
383+
client: client,
384+
chain: options.chain,
385+
});
386+
387+
// Disconnect the active wallet when the EOA disconnects if it the active wallet is a smart wallet
388+
const disconnectUnsub = eoaWallet.subscribe("disconnect", () => {
389+
handleDisconnect();
390+
});
391+
const handleDisconnect = () => {
392+
disconnectUnsub();
393+
onWalletDisconnect(wallet);
394+
};
395+
396+
return wallet;
397+
};

0 commit comments

Comments
 (0)