Skip to content

Commit c745b51

Browse files
test: add unit tests for transports (#63)
* test: update tests and mocks * test: add e2e tests for transports * test: fix tests * test: clean up comments * test: update test suite names * test: update test suite names
1 parent f7a36dd commit c745b51

File tree

3 files changed

+459
-0
lines changed

3 files changed

+459
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { type MockPort, mockSession } from '../../tests/mocks';
3+
import * as metamaskExtensionId from '../helpers/metamaskExtensionId';
4+
import { TransportError } from '../types/errors';
5+
import { getExternallyConnectableTransport } from './externallyConnectableTransport';
6+
7+
const testExtensionId = 'metamask-extension-id';
8+
9+
const mockChrome = {
10+
runtime: {
11+
connect: vi.fn(),
12+
},
13+
};
14+
15+
// @ts-ignore - Mocking chrome global
16+
global.chrome = mockChrome;
17+
18+
describe('ExternallyConnectableTransport', () => {
19+
let transport: ReturnType<typeof getExternallyConnectableTransport>;
20+
let messageHandler: (msg: any) => void;
21+
let disconnectHandler: () => void;
22+
23+
let mockPort: MockPort;
24+
beforeEach(() => {
25+
vi.clearAllMocks();
26+
27+
// Setup mock port
28+
mockPort = {
29+
postMessage: vi.fn(),
30+
onMessage: {
31+
addListener: vi.fn((handler: (msg: any) => void) => {
32+
messageHandler = handler;
33+
}),
34+
},
35+
onDisconnect: {
36+
addListener: vi.fn((handler: () => void) => {
37+
disconnectHandler = handler;
38+
}),
39+
},
40+
disconnect: vi.fn(),
41+
};
42+
43+
// Setup chrome.runtime.connect mock
44+
mockChrome.runtime.connect.mockReturnValue(mockPort);
45+
46+
// Create transport instance
47+
transport = getExternallyConnectableTransport({ extensionId: testExtensionId });
48+
});
49+
50+
afterEach(async () => {
51+
await transport.disconnect();
52+
});
53+
54+
it('should fetch extension id when not provided', async () => {
55+
const detectSpy = vi.spyOn(metamaskExtensionId, 'detectMetamaskExtensionId').mockResolvedValue(testExtensionId);
56+
57+
const newTransport = getExternallyConnectableTransport({});
58+
await newTransport.connect();
59+
60+
expect(detectSpy).toHaveBeenCalled();
61+
expect(mockChrome.runtime.connect).toHaveBeenCalledWith(testExtensionId);
62+
63+
detectSpy.mockRestore();
64+
});
65+
66+
it('should connect successfully', async () => {
67+
await transport.connect();
68+
expect(mockChrome.runtime.connect).toHaveBeenCalledWith(testExtensionId);
69+
expect(mockPort.onMessage.addListener).toHaveBeenCalled();
70+
expect(mockPort.onDisconnect.addListener).toHaveBeenCalled();
71+
});
72+
73+
it('should handle requests and responses', async () => {
74+
await transport.connect();
75+
const requestPromise = transport.request({
76+
method: 'wallet_getSession',
77+
});
78+
79+
// Simulate response
80+
messageHandler({
81+
type: 'caip-348',
82+
data: {
83+
id: 1,
84+
jsonrpc: '2.0',
85+
result: mockSession,
86+
},
87+
});
88+
89+
const response = await requestPromise;
90+
expect(response.result).toEqual(mockSession);
91+
expect(mockPort.postMessage).toHaveBeenCalledWith({
92+
type: 'caip-348',
93+
data: {
94+
id: 1,
95+
jsonrpc: '2.0',
96+
method: 'wallet_getSession',
97+
},
98+
});
99+
});
100+
101+
it('should handle notifications', async () => {
102+
await transport.connect();
103+
104+
const notificationCallback = vi.fn();
105+
transport.onNotification(notificationCallback);
106+
messageHandler!({
107+
data: {
108+
id: null,
109+
method: 'accountsChanged',
110+
params: ['0x123'],
111+
},
112+
});
113+
114+
expect(notificationCallback).toHaveBeenCalledWith({
115+
id: null,
116+
method: 'accountsChanged',
117+
params: ['0x123'],
118+
});
119+
});
120+
121+
it('should handle disconnection', async () => {
122+
await transport.connect();
123+
disconnectHandler();
124+
125+
expect(transport.isConnected()).toBe(false);
126+
});
127+
128+
it('should throw error when making request while disconnected', async () => {
129+
expect(() => transport.request({ method: 'wallet_getSession' })).toThrow(
130+
new TransportError('Chrome port not connected'),
131+
);
132+
});
133+
134+
it('should handle connection errors', async () => {
135+
mockChrome.runtime.connect.mockImplementation(() => {
136+
throw new Error('Connection failed');
137+
});
138+
139+
const error = await transport.connect().catch((e) => e);
140+
expect(error).toBeInstanceOf(TransportError);
141+
expect(error.message).toBe('Failed to connect to MetaMask');
142+
});
143+
});

0 commit comments

Comments
 (0)