@@ -6,7 +6,10 @@ import type { ThirdwebClient } from "../../client/client.js";
66import type { AsyncStorage } from "../../utils/storage/AsyncStorage.js" ;
77import type { Account , Wallet } from "../interfaces/wallet.js" ;
88import type { SmartWalletOptions } from "../smart/types.js" ;
9- import { createConnectionManager } from "./index.js" ;
9+ import {
10+ createConnectionManager ,
11+ handleSmartWalletConnection ,
12+ } from "./index.js" ;
1013
1114describe . 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} ) ;
0 commit comments