@etherplay/connect 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +20 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +954 -0
- package/dist/index.test.js.map +1 -0
- package/dist/popup.test.d.ts +2 -0
- package/dist/popup.test.d.ts.map +1 -0
- package/dist/popup.test.js +207 -0
- package/dist/popup.test.js.map +1 -0
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +112 -0
- package/dist/utils.test.js.map +1 -0
- package/package.json +8 -3
- package/src/index.ts +71 -32
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { createConnection, isTargetStepReached } from './index.js';
|
|
3
|
+
// Mock the @etherplay/alchemy module
|
|
4
|
+
vi.mock('@etherplay/alchemy', () => ({
|
|
5
|
+
fromEntropyKeyToMnemonic: vi.fn((key) => 'test mnemonic words here'),
|
|
6
|
+
fromSignatureToKey: vi.fn((signature) => '0x1234567890abcdef'),
|
|
7
|
+
originKeyMessage: vi.fn((origin) => `Sign this message to prove ownership: ${origin}`),
|
|
8
|
+
originPublicKeyPublicationMessage: vi.fn((origin, publicKey) => `Publish public key: ${publicKey} for ${origin}`),
|
|
9
|
+
}));
|
|
10
|
+
// Create a mock wallet provider
|
|
11
|
+
function createMockWalletProvider(accounts = [], chainId = '0x1') {
|
|
12
|
+
const accountsChangedListeners = new Set();
|
|
13
|
+
const chainChangedListeners = new Set();
|
|
14
|
+
const underlyingProvider = {
|
|
15
|
+
request: vi.fn(),
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
underlyingProvider,
|
|
19
|
+
signMessage: vi.fn().mockResolvedValue('0xsignature1234'),
|
|
20
|
+
getChainId: vi.fn().mockResolvedValue(chainId),
|
|
21
|
+
requestAccounts: vi.fn().mockResolvedValue(accounts),
|
|
22
|
+
getAccounts: vi.fn().mockResolvedValue(accounts),
|
|
23
|
+
listenForAccountsChanged: vi.fn((handler) => {
|
|
24
|
+
accountsChangedListeners.add(handler);
|
|
25
|
+
}),
|
|
26
|
+
stopListenForAccountsChanged: vi.fn((handler) => {
|
|
27
|
+
accountsChangedListeners.delete(handler);
|
|
28
|
+
}),
|
|
29
|
+
listenForChainChanged: vi.fn((handler) => {
|
|
30
|
+
chainChangedListeners.add(handler);
|
|
31
|
+
}),
|
|
32
|
+
stopListenForChainChanged: vi.fn((handler) => {
|
|
33
|
+
chainChangedListeners.delete(handler);
|
|
34
|
+
}),
|
|
35
|
+
switchChain: vi.fn().mockResolvedValue(null),
|
|
36
|
+
addChain: vi.fn().mockResolvedValue(null),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Create a mock wallet handle
|
|
40
|
+
function createMockWalletHandle(name = 'MockWallet', accounts = ['0xabc123'], chainId = '0x1') {
|
|
41
|
+
return {
|
|
42
|
+
info: {
|
|
43
|
+
uuid: `uuid-${name}`,
|
|
44
|
+
name,
|
|
45
|
+
icon: 'data:image/svg+xml,...',
|
|
46
|
+
rdns: `com.mock.${name.toLowerCase()}`,
|
|
47
|
+
},
|
|
48
|
+
walletProvider: createMockWalletProvider(accounts, chainId),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Create a mock account generator
|
|
52
|
+
function createMockAccountGenerator() {
|
|
53
|
+
return {
|
|
54
|
+
type: 'secp256k1',
|
|
55
|
+
fromMnemonicToAccount: vi.fn((mnemonic, index) => ({
|
|
56
|
+
address: '0xoriginaddress',
|
|
57
|
+
publicKey: '0xpublickey',
|
|
58
|
+
privateKey: '0xprivatekey',
|
|
59
|
+
})),
|
|
60
|
+
signTextMessage: vi.fn().mockResolvedValue('0xsig'),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Create a mock wallet connector
|
|
64
|
+
function createMockWalletConnector(walletHandles = []) {
|
|
65
|
+
const accountGenerator = createMockAccountGenerator();
|
|
66
|
+
const alwaysOnProvider = {
|
|
67
|
+
chainId: '1',
|
|
68
|
+
provider: { request: vi.fn() },
|
|
69
|
+
setWalletProvider: vi.fn(),
|
|
70
|
+
setWalletStatus: vi.fn(),
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
fetchWallets: vi.fn((callback) => {
|
|
74
|
+
// Simulate async wallet announcement
|
|
75
|
+
walletHandles.forEach((handle, index) => {
|
|
76
|
+
setTimeout(() => callback(handle), index * 10);
|
|
77
|
+
});
|
|
78
|
+
}),
|
|
79
|
+
createAlwaysOnProvider: vi.fn(() => alwaysOnProvider),
|
|
80
|
+
accountGenerator,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Default chain info for tests
|
|
84
|
+
const defaultChainInfo = {
|
|
85
|
+
id: 1,
|
|
86
|
+
name: 'Ethereum Mainnet',
|
|
87
|
+
rpcUrls: {
|
|
88
|
+
default: {
|
|
89
|
+
http: ['https://eth-mainnet.example.com'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
nativeCurrency: {
|
|
93
|
+
name: 'Ether',
|
|
94
|
+
symbol: 'ETH',
|
|
95
|
+
decimals: 18,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
describe('isTargetStepReached', () => {
|
|
99
|
+
describe('with targetStep: SignedIn', () => {
|
|
100
|
+
it('should return true when step is SignedIn with popup-based auth', () => {
|
|
101
|
+
const connection = {
|
|
102
|
+
step: 'SignedIn',
|
|
103
|
+
mechanism: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
104
|
+
account: {
|
|
105
|
+
address: '0x123',
|
|
106
|
+
signer: {
|
|
107
|
+
origin: 'test',
|
|
108
|
+
address: '0xorigin',
|
|
109
|
+
publicKey: '0xpub',
|
|
110
|
+
privateKey: '0xpriv',
|
|
111
|
+
mnemonicKey: '0xmnem',
|
|
112
|
+
},
|
|
113
|
+
metadata: {},
|
|
114
|
+
mechanismUsed: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
115
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
116
|
+
accountType: 'secp256k1',
|
|
117
|
+
},
|
|
118
|
+
wallet: undefined,
|
|
119
|
+
wallets: [],
|
|
120
|
+
};
|
|
121
|
+
expect(isTargetStepReached(connection, 'SignedIn')).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
it('should return true when step is SignedIn with wallet-based auth', () => {
|
|
124
|
+
const mockWalletProvider = createMockWalletProvider(['0xabc']);
|
|
125
|
+
const walletMechanism = { type: 'wallet', name: 'MockWallet', address: '0xabc' };
|
|
126
|
+
const connection = {
|
|
127
|
+
step: 'SignedIn',
|
|
128
|
+
mechanism: walletMechanism,
|
|
129
|
+
account: {
|
|
130
|
+
address: '0xabc',
|
|
131
|
+
signer: {
|
|
132
|
+
origin: 'test',
|
|
133
|
+
address: '0xorigin',
|
|
134
|
+
publicKey: '0xpub',
|
|
135
|
+
privateKey: '0xpriv',
|
|
136
|
+
mnemonicKey: '0xmnem',
|
|
137
|
+
},
|
|
138
|
+
metadata: {},
|
|
139
|
+
mechanismUsed: walletMechanism,
|
|
140
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
141
|
+
accountType: 'secp256k1',
|
|
142
|
+
},
|
|
143
|
+
wallet: {
|
|
144
|
+
provider: mockWalletProvider,
|
|
145
|
+
accounts: ['0xabc'],
|
|
146
|
+
status: 'connected',
|
|
147
|
+
chainId: '1',
|
|
148
|
+
invalidChainId: false,
|
|
149
|
+
switchingChain: false,
|
|
150
|
+
},
|
|
151
|
+
wallets: [],
|
|
152
|
+
};
|
|
153
|
+
expect(isTargetStepReached(connection, 'SignedIn')).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('should return false when step is not SignedIn', () => {
|
|
156
|
+
const connection = {
|
|
157
|
+
step: 'Idle',
|
|
158
|
+
loading: false,
|
|
159
|
+
wallet: undefined,
|
|
160
|
+
wallets: [],
|
|
161
|
+
};
|
|
162
|
+
expect(isTargetStepReached(connection, 'SignedIn')).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
it('should return false when step is WalletConnected', () => {
|
|
165
|
+
const mockWalletProvider = createMockWalletProvider(['0xabc']);
|
|
166
|
+
const connection = {
|
|
167
|
+
step: 'WalletConnected',
|
|
168
|
+
mechanism: { type: 'wallet', name: 'MockWallet', address: '0xabc' },
|
|
169
|
+
account: { address: '0xabc' },
|
|
170
|
+
wallet: {
|
|
171
|
+
provider: mockWalletProvider,
|
|
172
|
+
accounts: ['0xabc'],
|
|
173
|
+
status: 'connected',
|
|
174
|
+
chainId: '1',
|
|
175
|
+
invalidChainId: false,
|
|
176
|
+
switchingChain: false,
|
|
177
|
+
},
|
|
178
|
+
wallets: [],
|
|
179
|
+
};
|
|
180
|
+
expect(isTargetStepReached(connection, 'SignedIn')).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('with targetStep: WalletConnected', () => {
|
|
184
|
+
it('should return true when step is WalletConnected', () => {
|
|
185
|
+
const mockWalletProvider = createMockWalletProvider(['0xabc']);
|
|
186
|
+
const connection = {
|
|
187
|
+
step: 'WalletConnected',
|
|
188
|
+
mechanism: { type: 'wallet', name: 'MockWallet', address: '0xabc' },
|
|
189
|
+
account: { address: '0xabc' },
|
|
190
|
+
wallet: {
|
|
191
|
+
provider: mockWalletProvider,
|
|
192
|
+
accounts: ['0xabc'],
|
|
193
|
+
status: 'connected',
|
|
194
|
+
chainId: '1',
|
|
195
|
+
invalidChainId: false,
|
|
196
|
+
switchingChain: false,
|
|
197
|
+
},
|
|
198
|
+
wallets: [],
|
|
199
|
+
};
|
|
200
|
+
expect(isTargetStepReached(connection, 'WalletConnected')).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
it('should return true when step is SignedIn with wallet', () => {
|
|
203
|
+
const mockWalletProvider = createMockWalletProvider(['0xabc']);
|
|
204
|
+
const walletMechanism2 = { type: 'wallet', name: 'MockWallet', address: '0xabc' };
|
|
205
|
+
const connection = {
|
|
206
|
+
step: 'SignedIn',
|
|
207
|
+
mechanism: walletMechanism2,
|
|
208
|
+
account: {
|
|
209
|
+
address: '0xabc',
|
|
210
|
+
signer: {
|
|
211
|
+
origin: 'test',
|
|
212
|
+
address: '0xorigin',
|
|
213
|
+
publicKey: '0xpub',
|
|
214
|
+
privateKey: '0xpriv',
|
|
215
|
+
mnemonicKey: '0xmnem',
|
|
216
|
+
},
|
|
217
|
+
metadata: {},
|
|
218
|
+
mechanismUsed: walletMechanism2,
|
|
219
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
220
|
+
accountType: 'secp256k1',
|
|
221
|
+
},
|
|
222
|
+
wallet: {
|
|
223
|
+
provider: mockWalletProvider,
|
|
224
|
+
accounts: ['0xabc'],
|
|
225
|
+
status: 'connected',
|
|
226
|
+
chainId: '1',
|
|
227
|
+
invalidChainId: false,
|
|
228
|
+
switchingChain: false,
|
|
229
|
+
},
|
|
230
|
+
wallets: [],
|
|
231
|
+
};
|
|
232
|
+
expect(isTargetStepReached(connection, 'WalletConnected')).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
it('should return false when step is SignedIn without wallet', () => {
|
|
235
|
+
const connection = {
|
|
236
|
+
step: 'SignedIn',
|
|
237
|
+
mechanism: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
238
|
+
account: {
|
|
239
|
+
address: '0x123',
|
|
240
|
+
signer: {
|
|
241
|
+
origin: 'test',
|
|
242
|
+
address: '0xorigin',
|
|
243
|
+
publicKey: '0xpub',
|
|
244
|
+
privateKey: '0xpriv',
|
|
245
|
+
mnemonicKey: '0xmnem',
|
|
246
|
+
},
|
|
247
|
+
metadata: {},
|
|
248
|
+
mechanismUsed: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
249
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
250
|
+
accountType: 'secp256k1',
|
|
251
|
+
},
|
|
252
|
+
wallet: undefined,
|
|
253
|
+
wallets: [],
|
|
254
|
+
};
|
|
255
|
+
expect(isTargetStepReached(connection, 'WalletConnected')).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
it('should return false when step is Idle', () => {
|
|
258
|
+
const connection = {
|
|
259
|
+
step: 'Idle',
|
|
260
|
+
loading: false,
|
|
261
|
+
wallet: undefined,
|
|
262
|
+
wallets: [],
|
|
263
|
+
};
|
|
264
|
+
expect(isTargetStepReached(connection, 'WalletConnected')).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('with walletOnly: true', () => {
|
|
268
|
+
it('should return true for SignedIn with wallet when walletOnly is true', () => {
|
|
269
|
+
const mockWalletProvider = createMockWalletProvider(['0xabc']);
|
|
270
|
+
const walletMechanism3 = { type: 'wallet', name: 'MockWallet', address: '0xabc' };
|
|
271
|
+
const connection = {
|
|
272
|
+
step: 'SignedIn',
|
|
273
|
+
mechanism: walletMechanism3,
|
|
274
|
+
account: {
|
|
275
|
+
address: '0xabc',
|
|
276
|
+
signer: {
|
|
277
|
+
origin: 'test',
|
|
278
|
+
address: '0xorigin',
|
|
279
|
+
publicKey: '0xpub',
|
|
280
|
+
privateKey: '0xpriv',
|
|
281
|
+
mnemonicKey: '0xmnem',
|
|
282
|
+
},
|
|
283
|
+
metadata: {},
|
|
284
|
+
mechanismUsed: walletMechanism3,
|
|
285
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
286
|
+
accountType: 'secp256k1',
|
|
287
|
+
},
|
|
288
|
+
wallet: {
|
|
289
|
+
provider: mockWalletProvider,
|
|
290
|
+
accounts: ['0xabc'],
|
|
291
|
+
status: 'connected',
|
|
292
|
+
chainId: '1',
|
|
293
|
+
invalidChainId: false,
|
|
294
|
+
switchingChain: false,
|
|
295
|
+
},
|
|
296
|
+
wallets: [],
|
|
297
|
+
};
|
|
298
|
+
expect(isTargetStepReached(connection, 'SignedIn', true)).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
it('should return false for SignedIn without wallet when walletOnly is true', () => {
|
|
301
|
+
const connection = {
|
|
302
|
+
step: 'SignedIn',
|
|
303
|
+
mechanism: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
304
|
+
account: {
|
|
305
|
+
address: '0x123',
|
|
306
|
+
signer: {
|
|
307
|
+
origin: 'test',
|
|
308
|
+
address: '0xorigin',
|
|
309
|
+
publicKey: '0xpub',
|
|
310
|
+
privateKey: '0xpriv',
|
|
311
|
+
mnemonicKey: '0xmnem',
|
|
312
|
+
},
|
|
313
|
+
metadata: {},
|
|
314
|
+
mechanismUsed: { type: 'email', email: 'test@example.com', mode: 'otp' },
|
|
315
|
+
savedPublicKeyPublicationSignature: undefined,
|
|
316
|
+
accountType: 'secp256k1',
|
|
317
|
+
},
|
|
318
|
+
wallet: undefined,
|
|
319
|
+
wallets: [],
|
|
320
|
+
};
|
|
321
|
+
expect(isTargetStepReached(connection, 'SignedIn', true)).toBe(false);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
describe('createConnection', () => {
|
|
326
|
+
beforeEach(() => {
|
|
327
|
+
// Clear localStorage before each test
|
|
328
|
+
localStorage.clear();
|
|
329
|
+
sessionStorage.clear();
|
|
330
|
+
vi.useFakeTimers();
|
|
331
|
+
});
|
|
332
|
+
afterEach(() => {
|
|
333
|
+
vi.clearAllMocks();
|
|
334
|
+
vi.useRealTimers();
|
|
335
|
+
});
|
|
336
|
+
describe('initialization', () => {
|
|
337
|
+
it('should create a connection store with required properties', () => {
|
|
338
|
+
const walletConnector = createMockWalletConnector();
|
|
339
|
+
const store = createConnection({
|
|
340
|
+
walletHost: 'https://wallet.example.com',
|
|
341
|
+
chainInfo: defaultChainInfo,
|
|
342
|
+
walletConnector,
|
|
343
|
+
});
|
|
344
|
+
expect(store).toBeDefined();
|
|
345
|
+
expect(typeof store.subscribe).toBe('function');
|
|
346
|
+
expect(typeof store.connect).toBe('function');
|
|
347
|
+
expect(typeof store.cancel).toBe('function');
|
|
348
|
+
expect(typeof store.back).toBe('function');
|
|
349
|
+
expect(typeof store.disconnect).toBe('function');
|
|
350
|
+
expect(typeof store.requestSignature).toBe('function');
|
|
351
|
+
expect(typeof store.connectToAddress).toBe('function');
|
|
352
|
+
expect(typeof store.getSignatureForPublicKeyPublication).toBe('function');
|
|
353
|
+
expect(typeof store.switchWalletChain).toBe('function');
|
|
354
|
+
expect(typeof store.unlock).toBe('function');
|
|
355
|
+
expect(typeof store.ensureConnected).toBe('function');
|
|
356
|
+
expect(typeof store.isTargetStepReached).toBe('function');
|
|
357
|
+
});
|
|
358
|
+
it('should start in Idle step with loading true when autoConnect is true (with no saved session)', () => {
|
|
359
|
+
const walletConnector = createMockWalletConnector();
|
|
360
|
+
const store = createConnection({
|
|
361
|
+
walletHost: 'https://wallet.example.com',
|
|
362
|
+
chainInfo: defaultChainInfo,
|
|
363
|
+
walletConnector,
|
|
364
|
+
autoConnect: true,
|
|
365
|
+
});
|
|
366
|
+
let currentState;
|
|
367
|
+
store.subscribe((state) => {
|
|
368
|
+
currentState = state;
|
|
369
|
+
});
|
|
370
|
+
expect(currentState?.step).toBe('Idle');
|
|
371
|
+
// When no saved session exists, loading becomes false after initial check
|
|
372
|
+
// The loading: true state is very brief and may resolve to false immediately
|
|
373
|
+
expect(currentState?.step === 'Idle').toBe(true);
|
|
374
|
+
});
|
|
375
|
+
it('should start in Idle step with loading false when autoConnect is false', () => {
|
|
376
|
+
const walletConnector = createMockWalletConnector();
|
|
377
|
+
const store = createConnection({
|
|
378
|
+
walletHost: 'https://wallet.example.com',
|
|
379
|
+
chainInfo: defaultChainInfo,
|
|
380
|
+
walletConnector,
|
|
381
|
+
autoConnect: false,
|
|
382
|
+
});
|
|
383
|
+
let currentState;
|
|
384
|
+
store.subscribe((state) => {
|
|
385
|
+
currentState = state;
|
|
386
|
+
});
|
|
387
|
+
expect(currentState?.step).toBe('Idle');
|
|
388
|
+
expect(currentState?.step === 'Idle' && currentState.loading).toBe(false);
|
|
389
|
+
});
|
|
390
|
+
it('should expose chainId and chainInfo', () => {
|
|
391
|
+
const walletConnector = createMockWalletConnector();
|
|
392
|
+
const store = createConnection({
|
|
393
|
+
walletHost: 'https://wallet.example.com',
|
|
394
|
+
chainInfo: defaultChainInfo,
|
|
395
|
+
walletConnector,
|
|
396
|
+
});
|
|
397
|
+
expect(store.chainId).toBe('1');
|
|
398
|
+
expect(store.chainInfo).toEqual(defaultChainInfo);
|
|
399
|
+
});
|
|
400
|
+
it('should set targetStep to SignedIn by default', () => {
|
|
401
|
+
const walletConnector = createMockWalletConnector();
|
|
402
|
+
const store = createConnection({
|
|
403
|
+
walletHost: 'https://wallet.example.com',
|
|
404
|
+
chainInfo: defaultChainInfo,
|
|
405
|
+
walletConnector,
|
|
406
|
+
});
|
|
407
|
+
expect(store.targetStep).toBe('SignedIn');
|
|
408
|
+
});
|
|
409
|
+
it('should respect custom targetStep', () => {
|
|
410
|
+
const walletConnector = createMockWalletConnector();
|
|
411
|
+
const store = createConnection({
|
|
412
|
+
targetStep: 'WalletConnected',
|
|
413
|
+
chainInfo: defaultChainInfo,
|
|
414
|
+
walletConnector,
|
|
415
|
+
});
|
|
416
|
+
expect(store.targetStep).toBe('WalletConnected');
|
|
417
|
+
});
|
|
418
|
+
it('should set walletOnly based on settings', () => {
|
|
419
|
+
const walletConnector = createMockWalletConnector();
|
|
420
|
+
const store = createConnection({
|
|
421
|
+
walletHost: 'https://wallet.example.com',
|
|
422
|
+
walletOnly: true,
|
|
423
|
+
chainInfo: defaultChainInfo,
|
|
424
|
+
walletConnector,
|
|
425
|
+
});
|
|
426
|
+
expect(store.walletOnly).toBe(true);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
describe('connect with wallet mechanism', () => {
|
|
430
|
+
it('should transition to MechanismToChoose when connect is called without mechanism', async () => {
|
|
431
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
432
|
+
const store = createConnection({
|
|
433
|
+
walletHost: 'https://wallet.example.com',
|
|
434
|
+
chainInfo: defaultChainInfo,
|
|
435
|
+
walletConnector,
|
|
436
|
+
autoConnect: false,
|
|
437
|
+
});
|
|
438
|
+
let currentState;
|
|
439
|
+
store.subscribe((state) => {
|
|
440
|
+
currentState = state;
|
|
441
|
+
});
|
|
442
|
+
await store.connect();
|
|
443
|
+
expect(currentState?.step).toBe('MechanismToChoose');
|
|
444
|
+
});
|
|
445
|
+
it('should transition to WalletToChoose when connect is called with wallet mechanism but no specific wallet', async () => {
|
|
446
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle('Wallet1'), createMockWalletHandle('Wallet2')]);
|
|
447
|
+
const store = createConnection({
|
|
448
|
+
walletHost: 'https://wallet.example.com',
|
|
449
|
+
chainInfo: defaultChainInfo,
|
|
450
|
+
walletConnector,
|
|
451
|
+
autoConnect: false,
|
|
452
|
+
});
|
|
453
|
+
// Wait for wallets to be fetched
|
|
454
|
+
vi.advanceTimersByTime(50);
|
|
455
|
+
let currentState;
|
|
456
|
+
store.subscribe((state) => {
|
|
457
|
+
currentState = state;
|
|
458
|
+
});
|
|
459
|
+
await store.connect({ type: 'wallet' });
|
|
460
|
+
expect(currentState?.step).toBe('WalletToChoose');
|
|
461
|
+
});
|
|
462
|
+
it('should transition to WalletConnected when single wallet connects successfully', async () => {
|
|
463
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
464
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
465
|
+
const store = createConnection({
|
|
466
|
+
walletHost: 'https://wallet.example.com',
|
|
467
|
+
chainInfo: defaultChainInfo,
|
|
468
|
+
walletConnector,
|
|
469
|
+
autoConnect: false,
|
|
470
|
+
});
|
|
471
|
+
// Wait for wallet to be announced
|
|
472
|
+
vi.advanceTimersByTime(50);
|
|
473
|
+
let currentState;
|
|
474
|
+
store.subscribe((state) => {
|
|
475
|
+
currentState = state;
|
|
476
|
+
});
|
|
477
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
478
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
479
|
+
await connectPromise;
|
|
480
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
481
|
+
if (currentState?.step === 'WalletConnected') {
|
|
482
|
+
expect(currentState.mechanism.name).toBe('MockWallet');
|
|
483
|
+
expect(currentState.mechanism.address).toBe('0xuser123');
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
it('should transition to ChooseWalletAccount when multiple accounts available', async () => {
|
|
487
|
+
const mockHandle = createMockWalletHandle('MockWallet', [
|
|
488
|
+
'0xuser1',
|
|
489
|
+
'0xuser2',
|
|
490
|
+
]);
|
|
491
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
492
|
+
const store = createConnection({
|
|
493
|
+
walletHost: 'https://wallet.example.com',
|
|
494
|
+
chainInfo: defaultChainInfo,
|
|
495
|
+
walletConnector,
|
|
496
|
+
autoConnect: false,
|
|
497
|
+
alwaysUseCurrentAccount: false,
|
|
498
|
+
});
|
|
499
|
+
// Wait for wallet to be announced
|
|
500
|
+
vi.advanceTimersByTime(50);
|
|
501
|
+
let currentState;
|
|
502
|
+
store.subscribe((state) => {
|
|
503
|
+
currentState = state;
|
|
504
|
+
});
|
|
505
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
506
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
507
|
+
await connectPromise;
|
|
508
|
+
expect(currentState?.step).toBe('ChooseWalletAccount');
|
|
509
|
+
if (currentState?.step === 'ChooseWalletAccount') {
|
|
510
|
+
expect(currentState.wallet.accounts).toEqual(['0xuser1', '0xuser2']);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
it('should skip account selection when alwaysUseCurrentAccount is true', async () => {
|
|
514
|
+
const mockHandle = createMockWalletHandle('MockWallet', [
|
|
515
|
+
'0xuser1',
|
|
516
|
+
'0xuser2',
|
|
517
|
+
]);
|
|
518
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
519
|
+
const store = createConnection({
|
|
520
|
+
walletHost: 'https://wallet.example.com',
|
|
521
|
+
chainInfo: defaultChainInfo,
|
|
522
|
+
walletConnector,
|
|
523
|
+
autoConnect: false,
|
|
524
|
+
alwaysUseCurrentAccount: true,
|
|
525
|
+
});
|
|
526
|
+
// Wait for wallet to be announced
|
|
527
|
+
vi.advanceTimersByTime(50);
|
|
528
|
+
let currentState;
|
|
529
|
+
store.subscribe((state) => {
|
|
530
|
+
currentState = state;
|
|
531
|
+
});
|
|
532
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
533
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
534
|
+
await connectPromise;
|
|
535
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
536
|
+
});
|
|
537
|
+
it('should set error when wallet connection fails', async () => {
|
|
538
|
+
const mockHandle = createMockWalletHandle('MockWallet', []);
|
|
539
|
+
// Make requestAccounts return empty array to simulate connection failure
|
|
540
|
+
mockHandle.walletProvider.requestAccounts.mockResolvedValue([]);
|
|
541
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
542
|
+
const store = createConnection({
|
|
543
|
+
walletHost: 'https://wallet.example.com',
|
|
544
|
+
chainInfo: defaultChainInfo,
|
|
545
|
+
walletConnector,
|
|
546
|
+
autoConnect: false,
|
|
547
|
+
});
|
|
548
|
+
// Wait for wallet to be announced
|
|
549
|
+
vi.advanceTimersByTime(50);
|
|
550
|
+
let currentState;
|
|
551
|
+
store.subscribe((state) => {
|
|
552
|
+
currentState = state;
|
|
553
|
+
});
|
|
554
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
555
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
556
|
+
await connectPromise;
|
|
557
|
+
expect(currentState?.step).toBe('MechanismToChoose');
|
|
558
|
+
expect(currentState?.error?.message).toBe('could not get any accounts');
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
describe('disconnect', () => {
|
|
562
|
+
it('should transition to Idle and clear storage when disconnect is called', async () => {
|
|
563
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
564
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
565
|
+
const store = createConnection({
|
|
566
|
+
walletHost: 'https://wallet.example.com',
|
|
567
|
+
chainInfo: defaultChainInfo,
|
|
568
|
+
walletConnector,
|
|
569
|
+
autoConnect: false,
|
|
570
|
+
});
|
|
571
|
+
// Wait for wallet to be announced
|
|
572
|
+
vi.advanceTimersByTime(50);
|
|
573
|
+
let currentState;
|
|
574
|
+
store.subscribe((state) => {
|
|
575
|
+
currentState = state;
|
|
576
|
+
});
|
|
577
|
+
// Connect first
|
|
578
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
579
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
580
|
+
await connectPromise;
|
|
581
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
582
|
+
// Now disconnect
|
|
583
|
+
store.disconnect();
|
|
584
|
+
expect(currentState?.step).toBe('Idle');
|
|
585
|
+
if (currentState?.step === 'Idle') {
|
|
586
|
+
expect(currentState.loading).toBe(false);
|
|
587
|
+
expect(currentState.wallet).toBeUndefined();
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
describe('cancel', () => {
|
|
592
|
+
it('should transition to Idle when cancel is called', async () => {
|
|
593
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
594
|
+
const store = createConnection({
|
|
595
|
+
walletHost: 'https://wallet.example.com',
|
|
596
|
+
chainInfo: defaultChainInfo,
|
|
597
|
+
walletConnector,
|
|
598
|
+
autoConnect: false,
|
|
599
|
+
});
|
|
600
|
+
let currentState;
|
|
601
|
+
store.subscribe((state) => {
|
|
602
|
+
currentState = state;
|
|
603
|
+
});
|
|
604
|
+
await store.connect();
|
|
605
|
+
expect(currentState?.step).toBe('MechanismToChoose');
|
|
606
|
+
store.cancel();
|
|
607
|
+
expect(currentState?.step).toBe('Idle');
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
describe('back', () => {
|
|
611
|
+
it('should transition to MechanismToChoose when back is called with MechanismToChoose', async () => {
|
|
612
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle(), createMockWalletHandle('Wallet2')]);
|
|
613
|
+
const store = createConnection({
|
|
614
|
+
walletHost: 'https://wallet.example.com',
|
|
615
|
+
chainInfo: defaultChainInfo,
|
|
616
|
+
walletConnector,
|
|
617
|
+
autoConnect: false,
|
|
618
|
+
});
|
|
619
|
+
// Wait for wallets to be announced
|
|
620
|
+
vi.advanceTimersByTime(50);
|
|
621
|
+
let currentState;
|
|
622
|
+
store.subscribe((state) => {
|
|
623
|
+
currentState = state;
|
|
624
|
+
});
|
|
625
|
+
await store.connect({ type: 'wallet' });
|
|
626
|
+
expect(currentState?.step).toBe('WalletToChoose');
|
|
627
|
+
store.back('MechanismToChoose');
|
|
628
|
+
expect(currentState?.step).toBe('MechanismToChoose');
|
|
629
|
+
});
|
|
630
|
+
it('should transition to Idle when back is called with Idle', async () => {
|
|
631
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
632
|
+
const store = createConnection({
|
|
633
|
+
walletHost: 'https://wallet.example.com',
|
|
634
|
+
chainInfo: defaultChainInfo,
|
|
635
|
+
walletConnector,
|
|
636
|
+
autoConnect: false,
|
|
637
|
+
});
|
|
638
|
+
let currentState;
|
|
639
|
+
store.subscribe((state) => {
|
|
640
|
+
currentState = state;
|
|
641
|
+
});
|
|
642
|
+
await store.connect();
|
|
643
|
+
expect(currentState?.step).toBe('MechanismToChoose');
|
|
644
|
+
store.back('Idle');
|
|
645
|
+
expect(currentState?.step).toBe('Idle');
|
|
646
|
+
});
|
|
647
|
+
it('should transition to WalletToChoose when back is called with WalletToChoose', async () => {
|
|
648
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
649
|
+
const store = createConnection({
|
|
650
|
+
walletHost: 'https://wallet.example.com',
|
|
651
|
+
chainInfo: defaultChainInfo,
|
|
652
|
+
walletConnector,
|
|
653
|
+
autoConnect: false,
|
|
654
|
+
});
|
|
655
|
+
let currentState;
|
|
656
|
+
store.subscribe((state) => {
|
|
657
|
+
currentState = state;
|
|
658
|
+
});
|
|
659
|
+
await store.connect();
|
|
660
|
+
store.back('WalletToChoose');
|
|
661
|
+
expect(currentState?.step).toBe('WalletToChoose');
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
describe('connectToAddress', () => {
|
|
665
|
+
it('should connect to a specific address when wallet is connected', async () => {
|
|
666
|
+
const mockHandle = createMockWalletHandle('MockWallet', [
|
|
667
|
+
'0xuser1',
|
|
668
|
+
'0xuser2',
|
|
669
|
+
]);
|
|
670
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
671
|
+
const store = createConnection({
|
|
672
|
+
walletHost: 'https://wallet.example.com',
|
|
673
|
+
chainInfo: defaultChainInfo,
|
|
674
|
+
walletConnector,
|
|
675
|
+
autoConnect: false,
|
|
676
|
+
alwaysUseCurrentAccount: false,
|
|
677
|
+
});
|
|
678
|
+
// Wait for wallet to be announced
|
|
679
|
+
vi.advanceTimersByTime(50);
|
|
680
|
+
let currentState;
|
|
681
|
+
store.subscribe((state) => {
|
|
682
|
+
currentState = state;
|
|
683
|
+
});
|
|
684
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
685
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
686
|
+
await connectPromise;
|
|
687
|
+
expect(currentState?.step).toBe('ChooseWalletAccount');
|
|
688
|
+
// Now choose a specific address
|
|
689
|
+
store.connectToAddress('0xuser2');
|
|
690
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
691
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
692
|
+
if (currentState?.step === 'WalletConnected') {
|
|
693
|
+
expect(currentState.mechanism.address).toBe('0xuser2');
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
it('should throw error when no wallet is connected', () => {
|
|
697
|
+
const walletConnector = createMockWalletConnector([]);
|
|
698
|
+
const store = createConnection({
|
|
699
|
+
walletHost: 'https://wallet.example.com',
|
|
700
|
+
chainInfo: defaultChainInfo,
|
|
701
|
+
walletConnector,
|
|
702
|
+
autoConnect: false,
|
|
703
|
+
});
|
|
704
|
+
expect(() => store.connectToAddress('0xuser')).toThrow('need to be using a wallet');
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
describe('store.isTargetStepReached', () => {
|
|
708
|
+
it('should correctly check if target step is reached', async () => {
|
|
709
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
710
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
711
|
+
const store = createConnection({
|
|
712
|
+
targetStep: 'WalletConnected',
|
|
713
|
+
chainInfo: defaultChainInfo,
|
|
714
|
+
walletConnector,
|
|
715
|
+
autoConnect: false,
|
|
716
|
+
});
|
|
717
|
+
// Wait for wallet to be announced
|
|
718
|
+
vi.advanceTimersByTime(50);
|
|
719
|
+
let currentState;
|
|
720
|
+
store.subscribe((state) => {
|
|
721
|
+
currentState = state;
|
|
722
|
+
});
|
|
723
|
+
// Initially not reached
|
|
724
|
+
expect(store.isTargetStepReached(currentState)).toBe(false);
|
|
725
|
+
// Connect
|
|
726
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
727
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
728
|
+
await connectPromise;
|
|
729
|
+
// Now should be reached
|
|
730
|
+
expect(store.isTargetStepReached(currentState)).toBe(true);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
describe('chainId handling', () => {
|
|
734
|
+
it('should detect invalid chainId when wallet chain differs from configured chain', async () => {
|
|
735
|
+
// Create a wallet on chain 5 (Goerli) while our config is for chain 1 (Mainnet)
|
|
736
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123'], '0x5');
|
|
737
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
738
|
+
const store = createConnection({
|
|
739
|
+
walletHost: 'https://wallet.example.com',
|
|
740
|
+
chainInfo: defaultChainInfo, // Chain ID 1
|
|
741
|
+
walletConnector,
|
|
742
|
+
autoConnect: false,
|
|
743
|
+
});
|
|
744
|
+
// Wait for wallet to be announced
|
|
745
|
+
vi.advanceTimersByTime(50);
|
|
746
|
+
let currentState;
|
|
747
|
+
store.subscribe((state) => {
|
|
748
|
+
currentState = state;
|
|
749
|
+
});
|
|
750
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
751
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
752
|
+
await connectPromise;
|
|
753
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
754
|
+
if (currentState?.step === 'WalletConnected') {
|
|
755
|
+
expect(currentState.wallet.invalidChainId).toBe(true);
|
|
756
|
+
expect(currentState.wallet.chainId).toBe('5');
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
it('should not mark chainId as invalid when chains match', async () => {
|
|
760
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123'], '0x1');
|
|
761
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
762
|
+
const store = createConnection({
|
|
763
|
+
walletHost: 'https://wallet.example.com',
|
|
764
|
+
chainInfo: defaultChainInfo, // Chain ID 1
|
|
765
|
+
walletConnector,
|
|
766
|
+
autoConnect: false,
|
|
767
|
+
});
|
|
768
|
+
// Wait for wallet to be announced
|
|
769
|
+
vi.advanceTimersByTime(50);
|
|
770
|
+
let currentState;
|
|
771
|
+
store.subscribe((state) => {
|
|
772
|
+
currentState = state;
|
|
773
|
+
});
|
|
774
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
775
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
776
|
+
await connectPromise;
|
|
777
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
778
|
+
if (currentState?.step === 'WalletConnected') {
|
|
779
|
+
expect(currentState.wallet.invalidChainId).toBe(false);
|
|
780
|
+
expect(currentState.wallet.chainId).toBe('1');
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
describe('WalletConnected targetStep', () => {
|
|
785
|
+
it('should auto-connect with wallet mechanism when targetStep is WalletConnected', async () => {
|
|
786
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
787
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
788
|
+
const store = createConnection({
|
|
789
|
+
targetStep: 'WalletConnected',
|
|
790
|
+
chainInfo: defaultChainInfo,
|
|
791
|
+
walletConnector,
|
|
792
|
+
autoConnect: false,
|
|
793
|
+
});
|
|
794
|
+
// Wait for wallet to be announced
|
|
795
|
+
vi.advanceTimersByTime(50);
|
|
796
|
+
let currentState;
|
|
797
|
+
store.subscribe((state) => {
|
|
798
|
+
currentState = state;
|
|
799
|
+
});
|
|
800
|
+
// When calling connect() without mechanism, should default to wallet
|
|
801
|
+
// Specify wallet name to avoid WalletToChoose step
|
|
802
|
+
const connectPromise = store.connect({ type: 'wallet', name: 'MockWallet' });
|
|
803
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
804
|
+
await connectPromise;
|
|
805
|
+
// For single wallet with specified name, should go to WalletConnected directly
|
|
806
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
describe('ensureConnected', () => {
|
|
810
|
+
it('should resolve when already connected to target step', async () => {
|
|
811
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
812
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
813
|
+
const store = createConnection({
|
|
814
|
+
targetStep: 'WalletConnected',
|
|
815
|
+
chainInfo: defaultChainInfo,
|
|
816
|
+
walletConnector,
|
|
817
|
+
autoConnect: false,
|
|
818
|
+
});
|
|
819
|
+
// Wait for wallet to be announced
|
|
820
|
+
vi.advanceTimersByTime(50);
|
|
821
|
+
// Connect first
|
|
822
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
823
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
824
|
+
await connectPromise;
|
|
825
|
+
// Now ensureConnected should resolve immediately
|
|
826
|
+
const result = await store.ensureConnected();
|
|
827
|
+
expect(result.step).toBe('WalletConnected');
|
|
828
|
+
});
|
|
829
|
+
it('should start connection if not yet connected', async () => {
|
|
830
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
831
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
832
|
+
const store = createConnection({
|
|
833
|
+
targetStep: 'WalletConnected',
|
|
834
|
+
chainInfo: defaultChainInfo,
|
|
835
|
+
walletConnector,
|
|
836
|
+
autoConnect: false,
|
|
837
|
+
});
|
|
838
|
+
// Wait for wallet to be announced
|
|
839
|
+
vi.advanceTimersByTime(50);
|
|
840
|
+
// ensureConnected should start connection and resolve
|
|
841
|
+
const ensurePromise = store.ensureConnected();
|
|
842
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
843
|
+
const result = await ensurePromise;
|
|
844
|
+
expect(result.step).toBe('WalletConnected');
|
|
845
|
+
});
|
|
846
|
+
it('should reject when connection is cancelled', async () => {
|
|
847
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
848
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
849
|
+
const store = createConnection({
|
|
850
|
+
targetStep: 'WalletConnected',
|
|
851
|
+
chainInfo: defaultChainInfo,
|
|
852
|
+
walletConnector,
|
|
853
|
+
autoConnect: false,
|
|
854
|
+
});
|
|
855
|
+
// Wait for wallet to be announced
|
|
856
|
+
vi.advanceTimersByTime(50);
|
|
857
|
+
// Start ensureConnected
|
|
858
|
+
const ensurePromise = store.ensureConnected();
|
|
859
|
+
// Cancel the connection
|
|
860
|
+
store.cancel();
|
|
861
|
+
await expect(ensurePromise).rejects.toThrow('Connection cancelled');
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
describe('walletHost requirement', () => {
|
|
865
|
+
it('should not require walletHost for WalletConnected targetStep', () => {
|
|
866
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
867
|
+
// This should not throw
|
|
868
|
+
const store = createConnection({
|
|
869
|
+
targetStep: 'WalletConnected',
|
|
870
|
+
chainInfo: defaultChainInfo,
|
|
871
|
+
walletConnector,
|
|
872
|
+
});
|
|
873
|
+
expect(store).toBeDefined();
|
|
874
|
+
});
|
|
875
|
+
it('should not require walletHost when walletOnly is true', () => {
|
|
876
|
+
const walletConnector = createMockWalletConnector([createMockWalletHandle()]);
|
|
877
|
+
// This should not throw
|
|
878
|
+
const store = createConnection({
|
|
879
|
+
walletOnly: true,
|
|
880
|
+
chainInfo: defaultChainInfo,
|
|
881
|
+
walletConnector,
|
|
882
|
+
});
|
|
883
|
+
expect(store).toBeDefined();
|
|
884
|
+
});
|
|
885
|
+
it('should throw when using popup-based auth without walletHost', async () => {
|
|
886
|
+
const walletConnector = createMockWalletConnector([]);
|
|
887
|
+
const store = createConnection({
|
|
888
|
+
walletHost: undefined, // Force undefined to test
|
|
889
|
+
chainInfo: defaultChainInfo,
|
|
890
|
+
walletConnector,
|
|
891
|
+
autoConnect: false,
|
|
892
|
+
});
|
|
893
|
+
// Attempting popup-based auth should fail
|
|
894
|
+
await expect(store.connect({ type: 'email', email: 'test@example.com', mode: 'otp' })).rejects.toThrow('walletHost is required for popup-based authentication');
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
describe('wallet state tracking', () => {
|
|
899
|
+
beforeEach(() => {
|
|
900
|
+
localStorage.clear();
|
|
901
|
+
sessionStorage.clear();
|
|
902
|
+
vi.useFakeTimers();
|
|
903
|
+
});
|
|
904
|
+
afterEach(() => {
|
|
905
|
+
vi.clearAllMocks();
|
|
906
|
+
vi.useRealTimers();
|
|
907
|
+
});
|
|
908
|
+
it('should track wallet status correctly', async () => {
|
|
909
|
+
const mockHandle = createMockWalletHandle('MockWallet', ['0xuser123']);
|
|
910
|
+
const walletConnector = createMockWalletConnector([mockHandle]);
|
|
911
|
+
const store = createConnection({
|
|
912
|
+
walletHost: 'https://wallet.example.com',
|
|
913
|
+
chainInfo: defaultChainInfo,
|
|
914
|
+
walletConnector,
|
|
915
|
+
autoConnect: false,
|
|
916
|
+
});
|
|
917
|
+
// Wait for wallet to be announced
|
|
918
|
+
vi.advanceTimersByTime(50);
|
|
919
|
+
let currentState;
|
|
920
|
+
store.subscribe((state) => {
|
|
921
|
+
currentState = state;
|
|
922
|
+
});
|
|
923
|
+
const connectPromise = store.connect({ type: 'wallet' });
|
|
924
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
925
|
+
await connectPromise;
|
|
926
|
+
expect(currentState?.step).toBe('WalletConnected');
|
|
927
|
+
if (currentState?.step === 'WalletConnected') {
|
|
928
|
+
expect(currentState.wallet.status).toBe('connected');
|
|
929
|
+
expect(currentState.wallet.accounts).toContain('0xuser123');
|
|
930
|
+
expect(currentState.wallet.switchingChain).toBe(false);
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
it('should populate wallets array when wallets are announced', async () => {
|
|
934
|
+
const handle1 = createMockWalletHandle('Wallet1', ['0x111']);
|
|
935
|
+
const handle2 = createMockWalletHandle('Wallet2', ['0x222']);
|
|
936
|
+
const walletConnector = createMockWalletConnector([handle1, handle2]);
|
|
937
|
+
const store = createConnection({
|
|
938
|
+
walletHost: 'https://wallet.example.com',
|
|
939
|
+
chainInfo: defaultChainInfo,
|
|
940
|
+
walletConnector,
|
|
941
|
+
autoConnect: false,
|
|
942
|
+
});
|
|
943
|
+
let currentState;
|
|
944
|
+
store.subscribe((state) => {
|
|
945
|
+
currentState = state;
|
|
946
|
+
});
|
|
947
|
+
// Wait for wallets to be announced
|
|
948
|
+
vi.advanceTimersByTime(50);
|
|
949
|
+
expect(currentState?.wallets.length).toBe(2);
|
|
950
|
+
expect(currentState?.wallets.map((w) => w.info.name)).toContain('Wallet1');
|
|
951
|
+
expect(currentState?.wallets.map((w) => w.info.name)).toContain('Wallet2');
|
|
952
|
+
});
|
|
953
|
+
});
|
|
954
|
+
//# sourceMappingURL=index.test.js.map
|