@wutiange/log-listener-plugin 2.0.1-alpha.3 → 2.0.1
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/LICENSE +201 -201
- package/README.md +163 -163
- package/dist/src/HTTPInterceptor.js +2 -1
- package/dist/src/HTTPInterceptor.js.map +1 -1
- package/dist/src/__tests__/Server.test.js +4 -4
- package/dist/src/__tests__/Server.test.js.map +1 -1
- package/dist/src/__tests__/console.test.d.ts +1 -0
- package/dist/src/__tests__/console.test.js +29 -0
- package/dist/src/__tests__/console.test.js.map +1 -0
- package/dist/src/__tests__/utils.test.js +5 -1
- package/dist/src/__tests__/utils.test.js.map +1 -1
- package/dist/src/common.d.ts +1 -7
- package/dist/src/common.js +2 -14
- package/dist/src/common.js.map +1 -1
- package/dist/src/logPlugin.js +22 -17
- package/dist/src/logPlugin.js.map +1 -1
- package/dist/src/server.d.ts +21 -0
- package/dist/src/{Server.js → server.js} +47 -31
- package/dist/src/server.js.map +1 -0
- package/dist/src/utils.js +4 -4
- package/dist/src/utils.js.map +1 -1
- package/index.ts +3 -3
- package/package.json +59 -59
- package/src/HTTPInterceptor.ts +340 -339
- package/src/Server.ts +178 -167
- package/src/__mocks__/react-native/Libraries/Blob/FileReader.js +44 -44
- package/src/__mocks__/react-native/Libraries/Network/XHRInterceptor.js +39 -39
- package/src/__tests__/HTTPInterceptor.test.ts +322 -322
- package/src/__tests__/Server.test.ts +149 -149
- package/src/__tests__/utils.test.ts +113 -112
- package/src/common.ts +57 -70
- package/src/logPlugin.ts +231 -224
- package/src/logger.ts +14 -14
- package/src/utils.ts +112 -112
- package/tsconfig.json +26 -26
- package/dist/src/Server.d.ts +0 -20
- package/dist/src/Server.js.map +0 -1
@@ -1,322 +1,322 @@
|
|
1
|
-
import { httpInterceptor } from '../HTTPInterceptor';
|
2
|
-
import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor';
|
3
|
-
import { formDataToString } from '../utils';
|
4
|
-
|
5
|
-
// Mock dependencies
|
6
|
-
jest.mock('buffer', () => ({
|
7
|
-
Blob: jest.fn(),
|
8
|
-
}));
|
9
|
-
jest.mock('../utils', () => ({
|
10
|
-
createClassWithErrorHandling: jest.fn(Class => Class),
|
11
|
-
formDataToString: jest.fn(),
|
12
|
-
}));
|
13
|
-
|
14
|
-
// 在文件顶部添加这个模拟
|
15
|
-
class MockFormData {
|
16
|
-
private data: Record<string, string> = {};
|
17
|
-
|
18
|
-
append(key: string, value: string) {
|
19
|
-
this.data[key] = value;
|
20
|
-
}
|
21
|
-
|
22
|
-
get(key: string) {
|
23
|
-
return this.data[key];
|
24
|
-
}
|
25
|
-
|
26
|
-
// 可以根据需要添加更多方法
|
27
|
-
}
|
28
|
-
|
29
|
-
class MockBlob {
|
30
|
-
private content: string;
|
31
|
-
type: any
|
32
|
-
constructor(parts: any, options: any = {}) {
|
33
|
-
this.content = parts ? parts.join('') : '';
|
34
|
-
this.type = options.type || '';
|
35
|
-
}
|
36
|
-
|
37
|
-
text(): Promise<string> {
|
38
|
-
return Promise.resolve(this.content);
|
39
|
-
}
|
40
|
-
|
41
|
-
arrayBuffer() {
|
42
|
-
return Promise.resolve(new ArrayBuffer(0));
|
43
|
-
}
|
44
|
-
}
|
45
|
-
// 全局声明,以避免 TypeScript 错误
|
46
|
-
declare global {
|
47
|
-
// @ts-ignore
|
48
|
-
var FormData: typeof MockFormData;
|
49
|
-
}
|
50
|
-
|
51
|
-
// 在测试套件开始前设置全局 FormData
|
52
|
-
beforeAll(() => {
|
53
|
-
// @ts-ignore
|
54
|
-
global.FormData = MockFormData;
|
55
|
-
});
|
56
|
-
|
57
|
-
// 在测试套件结束后清理
|
58
|
-
afterAll(() => {
|
59
|
-
// @ts-ignore
|
60
|
-
delete global.FormData;
|
61
|
-
});
|
62
|
-
|
63
|
-
describe('HTTPInterceptor', () => {
|
64
|
-
beforeEach(() => {
|
65
|
-
jest.clearAllMocks();
|
66
|
-
console.warn = jest.fn();
|
67
|
-
httpInterceptor.reset();
|
68
|
-
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(false);
|
69
|
-
});
|
70
|
-
|
71
|
-
describe('enable', () => {
|
72
|
-
it('should enable interception', () => {
|
73
|
-
httpInterceptor.enable();
|
74
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
75
|
-
});
|
76
|
-
|
77
|
-
it('should not enable if already enabled', () => {
|
78
|
-
httpInterceptor.enable();
|
79
|
-
httpInterceptor.enable();
|
80
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalledTimes(1);
|
81
|
-
});
|
82
|
-
|
83
|
-
it('should handle ignored hosts', () => {
|
84
|
-
httpInterceptor.enable({ ignoredHosts: ['example.com'] });
|
85
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
86
|
-
});
|
87
|
-
|
88
|
-
it('should handle ignored patterns', () => {
|
89
|
-
httpInterceptor.enable({ ignoredPatterns: [/^GET https:\/\/test\.com/] });
|
90
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
91
|
-
});
|
92
|
-
|
93
|
-
it('should handle ignored URLs', () => {
|
94
|
-
httpInterceptor.enable({ ignoredUrls: ['https://example.com/api'] });
|
95
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
96
|
-
});
|
97
|
-
|
98
|
-
it('should warn if another interceptor is running', () => {
|
99
|
-
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(true);
|
100
|
-
console.warn = jest.fn();
|
101
|
-
httpInterceptor.enable();
|
102
|
-
expect(console.warn).toHaveBeenCalled();
|
103
|
-
});
|
104
|
-
|
105
|
-
it('should force enable if specified', () => {
|
106
|
-
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(true);
|
107
|
-
httpInterceptor.enable({ forceEnable: true });
|
108
|
-
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
109
|
-
});
|
110
|
-
});
|
111
|
-
|
112
|
-
describe('disable', () => {
|
113
|
-
it('should disable interception', () => {
|
114
|
-
httpInterceptor.enable();
|
115
|
-
httpInterceptor.disable();
|
116
|
-
expect(XHRInterceptor.disableInterception).toHaveBeenCalled();
|
117
|
-
});
|
118
|
-
|
119
|
-
it('should not disable if not enabled', () => {
|
120
|
-
httpInterceptor.disable();
|
121
|
-
expect(XHRInterceptor.disableInterception).not.toHaveBeenCalled();
|
122
|
-
});
|
123
|
-
});
|
124
|
-
|
125
|
-
describe('listeners', () => {
|
126
|
-
it('should add and remove listeners', () => {
|
127
|
-
const listener = jest.fn();
|
128
|
-
const removeListener = httpInterceptor.addListener('open', listener);
|
129
|
-
expect(httpInterceptor['userListeners'].length).toBe(1);
|
130
|
-
removeListener();
|
131
|
-
expect(httpInterceptor['userListeners'].length).toBe(0);
|
132
|
-
});
|
133
|
-
|
134
|
-
it('should not add duplicate listeners', () => {
|
135
|
-
const listener = jest.fn();
|
136
|
-
httpInterceptor.addListener('open', listener);
|
137
|
-
httpInterceptor.addListener('open', listener);
|
138
|
-
expect(httpInterceptor['userListeners'].length).toBe(1);
|
139
|
-
});
|
140
|
-
|
141
|
-
it('should remove specific listener', () => {
|
142
|
-
const listener1 = jest.fn();
|
143
|
-
const listener2 = jest.fn();
|
144
|
-
httpInterceptor.addListener('open', listener1);
|
145
|
-
httpInterceptor.addListener('open', listener2);
|
146
|
-
httpInterceptor.removeListener('open', listener1);
|
147
|
-
expect(httpInterceptor['userListeners'].length).toBe(1);
|
148
|
-
expect(httpInterceptor['userListeners'][0][1]).toBe(listener2);
|
149
|
-
});
|
150
|
-
|
151
|
-
it('should remove all listeners', () => {
|
152
|
-
httpInterceptor.addListener('open', jest.fn());
|
153
|
-
httpInterceptor.addListener('send', jest.fn());
|
154
|
-
httpInterceptor.removeAllListener();
|
155
|
-
expect(httpInterceptor['userListeners'].length).toBe(0);
|
156
|
-
});
|
157
|
-
});
|
158
|
-
|
159
|
-
describe('request handling', () => {
|
160
|
-
let openCallback: Function;
|
161
|
-
let requestHeaderCallback: Function;
|
162
|
-
let headerReceivedCallback: Function;
|
163
|
-
let sendCallback: Function;
|
164
|
-
let responseCallback: Function;
|
165
|
-
|
166
|
-
beforeEach(() => {
|
167
|
-
httpInterceptor.enable();
|
168
|
-
openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
169
|
-
requestHeaderCallback = (XHRInterceptor.setRequestHeaderCallback as jest.Mock).mock.calls[0][0];
|
170
|
-
headerReceivedCallback = (XHRInterceptor.setHeaderReceivedCallback as jest.Mock).mock.calls[0][0];
|
171
|
-
sendCallback = (XHRInterceptor.setSendCallback as jest.Mock).mock.calls[0][0];
|
172
|
-
responseCallback = (XHRInterceptor.setResponseCallback as jest.Mock).mock.calls[0][0];
|
173
|
-
});
|
174
|
-
|
175
|
-
it('should handle open event', () => {
|
176
|
-
const listener = jest.fn();
|
177
|
-
httpInterceptor.addListener('open', listener);
|
178
|
-
const xhr = {}
|
179
|
-
openCallback('GET', 'https://example.com', xhr);
|
180
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
181
|
-
method: 'GET',
|
182
|
-
url: 'https://example.com',
|
183
|
-
}));
|
184
|
-
});
|
185
|
-
|
186
|
-
it('should handle request header event', () => {
|
187
|
-
const listener = jest.fn();
|
188
|
-
httpInterceptor.addListener('requestHeader', listener);
|
189
|
-
const xhr = {}
|
190
|
-
openCallback('GET', 'https://example.com', xhr);
|
191
|
-
requestHeaderCallback('Content-Type', 'application/json', xhr);
|
192
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
193
|
-
requestHeaders: { 'Content-Type': 'application/json' },
|
194
|
-
}));
|
195
|
-
});
|
196
|
-
|
197
|
-
it('should handle header received event', () => {
|
198
|
-
const listener = jest.fn();
|
199
|
-
httpInterceptor.addListener('headerReceived', listener);
|
200
|
-
const xhr: {[key in string]: any} = {}
|
201
|
-
openCallback('GET', 'https://example.com', xhr);
|
202
|
-
xhr.responseHeaders = { 'Content-Type': 'application/json' }
|
203
|
-
headerReceivedCallback('application/json', 100, {}, xhr);
|
204
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
205
|
-
responseContentType: 'application/json',
|
206
|
-
responseSize: 100,
|
207
|
-
responseHeaders: { 'Content-Type': 'application/json' },
|
208
|
-
}));
|
209
|
-
});
|
210
|
-
|
211
|
-
it('should handle send event with JSON data', () => {
|
212
|
-
const listener = jest.fn();
|
213
|
-
httpInterceptor.addListener('send', listener);
|
214
|
-
const xhr = {}
|
215
|
-
openCallback('POST', 'https://example.com', xhr);
|
216
|
-
sendCallback(JSON.stringify({ key: 'value' }), xhr);
|
217
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
218
|
-
requestData: { key: 'value' },
|
219
|
-
}));
|
220
|
-
});
|
221
|
-
|
222
|
-
it('should handle send event with FormData', () => {
|
223
|
-
const listener = jest.fn();
|
224
|
-
httpInterceptor.addListener('send', listener);
|
225
|
-
const xhr = {}
|
226
|
-
openCallback('POST', 'https://example.com', xhr);
|
227
|
-
const formData = new FormData();
|
228
|
-
formData.append('key', 'value');
|
229
|
-
(formDataToString as jest.Mock).mockReturnValue('key=value');
|
230
|
-
sendCallback(formData, xhr);
|
231
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
232
|
-
requestData: 'key=value',
|
233
|
-
}));
|
234
|
-
});
|
235
|
-
|
236
|
-
it('should handle response event', async () => {
|
237
|
-
const listener = jest.fn();
|
238
|
-
httpInterceptor.addListener('response', listener);
|
239
|
-
const xhr = {}
|
240
|
-
openCallback('GET', 'https://example.com', xhr);
|
241
|
-
await responseCallback(200, 1000, { data: 'response' }, 'https://example.com', 'json', xhr);
|
242
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
243
|
-
status: 200,
|
244
|
-
timeout: 1000,
|
245
|
-
responseData: { data: 'response' },
|
246
|
-
responseURL: 'https://example.com',
|
247
|
-
responseType: 'json',
|
248
|
-
}));
|
249
|
-
});
|
250
|
-
|
251
|
-
it('should handle response event with blob data', async () => {
|
252
|
-
const listener = jest.fn();
|
253
|
-
httpInterceptor.addListener('response', listener);
|
254
|
-
const xhr = {}
|
255
|
-
openCallback('GET', 'https://example.com', xhr);
|
256
|
-
const mockBlob = new MockBlob(['blob content']);
|
257
|
-
await responseCallback(200, 1000, mockBlob, 'https://example.com', 'blob', xhr);
|
258
|
-
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
259
|
-
responseData: 'blob content',
|
260
|
-
}));
|
261
|
-
});
|
262
|
-
});
|
263
|
-
|
264
|
-
describe('error handling', () => {
|
265
|
-
beforeEach(() => {
|
266
|
-
httpInterceptor.enable();
|
267
|
-
})
|
268
|
-
it('should handle errors in listeners', async () => {
|
269
|
-
const errorListener = jest.fn(() => {
|
270
|
-
throw new Error('Listener error');
|
271
|
-
});
|
272
|
-
httpInterceptor.addListener('open', errorListener);
|
273
|
-
console.warn = jest.fn();
|
274
|
-
const xhr = {}
|
275
|
-
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
276
|
-
openCallback('GET', 'https://example.com', xhr);
|
277
|
-
|
278
|
-
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Listener error'));
|
279
|
-
});
|
280
|
-
});
|
281
|
-
|
282
|
-
describe('ignored requests', () => {
|
283
|
-
it('should ignore requests to ignored hosts', () => {
|
284
|
-
httpInterceptor.enable({ ignoredHosts: ['ignored.com'] });
|
285
|
-
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
286
|
-
const listener = jest.fn();
|
287
|
-
httpInterceptor.addListener('open', listener);
|
288
|
-
|
289
|
-
openCallback('GET', 'https://ignored.com', { uniqueId: '123' });
|
290
|
-
expect(listener).not.toHaveBeenCalled();
|
291
|
-
|
292
|
-
openCallback('GET', 'https://example.com', { uniqueId: '124' });
|
293
|
-
expect(listener).toHaveBeenCalled();
|
294
|
-
});
|
295
|
-
|
296
|
-
it('should ignore requests to ignored URLs', () => {
|
297
|
-
httpInterceptor.enable({ ignoredUrls: ['https://example.com/ignored'] });
|
298
|
-
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
299
|
-
const listener = jest.fn();
|
300
|
-
httpInterceptor.addListener('open', listener);
|
301
|
-
|
302
|
-
openCallback('GET', 'https://example.com/ignored', { uniqueId: '123' });
|
303
|
-
expect(listener).not.toHaveBeenCalled();
|
304
|
-
|
305
|
-
openCallback('GET', 'https://example.com/api', { uniqueId: '124' });
|
306
|
-
expect(listener).toHaveBeenCalled();
|
307
|
-
});
|
308
|
-
|
309
|
-
it('should ignore requests matching ignored patterns', () => {
|
310
|
-
httpInterceptor.enable({ ignoredPatterns: [/^GET https:\/\/test\.com/] });
|
311
|
-
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
312
|
-
const listener = jest.fn();
|
313
|
-
httpInterceptor.addListener('open', listener);
|
314
|
-
|
315
|
-
openCallback('GET', 'https://test.com/api', { uniqueId: '123' });
|
316
|
-
expect(listener).not.toHaveBeenCalled();
|
317
|
-
|
318
|
-
openCallback('POST', 'https://test.com/api', { uniqueId: '124' });
|
319
|
-
expect(listener).toHaveBeenCalled();
|
320
|
-
});
|
321
|
-
});
|
322
|
-
});
|
1
|
+
import { httpInterceptor } from '../HTTPInterceptor';
|
2
|
+
import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor';
|
3
|
+
import { formDataToString } from '../utils';
|
4
|
+
|
5
|
+
// Mock dependencies
|
6
|
+
jest.mock('buffer', () => ({
|
7
|
+
Blob: jest.fn(),
|
8
|
+
}));
|
9
|
+
jest.mock('../utils', () => ({
|
10
|
+
createClassWithErrorHandling: jest.fn(Class => Class),
|
11
|
+
formDataToString: jest.fn(),
|
12
|
+
}));
|
13
|
+
|
14
|
+
// 在文件顶部添加这个模拟
|
15
|
+
class MockFormData {
|
16
|
+
private data: Record<string, string> = {};
|
17
|
+
|
18
|
+
append(key: string, value: string) {
|
19
|
+
this.data[key] = value;
|
20
|
+
}
|
21
|
+
|
22
|
+
get(key: string) {
|
23
|
+
return this.data[key];
|
24
|
+
}
|
25
|
+
|
26
|
+
// 可以根据需要添加更多方法
|
27
|
+
}
|
28
|
+
|
29
|
+
class MockBlob {
|
30
|
+
private content: string;
|
31
|
+
type: any
|
32
|
+
constructor(parts: any, options: any = {}) {
|
33
|
+
this.content = parts ? parts.join('') : '';
|
34
|
+
this.type = options.type || '';
|
35
|
+
}
|
36
|
+
|
37
|
+
text(): Promise<string> {
|
38
|
+
return Promise.resolve(this.content);
|
39
|
+
}
|
40
|
+
|
41
|
+
arrayBuffer() {
|
42
|
+
return Promise.resolve(new ArrayBuffer(0));
|
43
|
+
}
|
44
|
+
}
|
45
|
+
// 全局声明,以避免 TypeScript 错误
|
46
|
+
declare global {
|
47
|
+
// @ts-ignore
|
48
|
+
var FormData: typeof MockFormData;
|
49
|
+
}
|
50
|
+
|
51
|
+
// 在测试套件开始前设置全局 FormData
|
52
|
+
beforeAll(() => {
|
53
|
+
// @ts-ignore
|
54
|
+
global.FormData = MockFormData;
|
55
|
+
});
|
56
|
+
|
57
|
+
// 在测试套件结束后清理
|
58
|
+
afterAll(() => {
|
59
|
+
// @ts-ignore
|
60
|
+
delete global.FormData;
|
61
|
+
});
|
62
|
+
|
63
|
+
describe('HTTPInterceptor', () => {
|
64
|
+
beforeEach(() => {
|
65
|
+
jest.clearAllMocks();
|
66
|
+
console.warn = jest.fn();
|
67
|
+
httpInterceptor.reset();
|
68
|
+
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(false);
|
69
|
+
});
|
70
|
+
|
71
|
+
describe('enable', () => {
|
72
|
+
it('should enable interception', () => {
|
73
|
+
httpInterceptor.enable();
|
74
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
75
|
+
});
|
76
|
+
|
77
|
+
it('should not enable if already enabled', () => {
|
78
|
+
httpInterceptor.enable();
|
79
|
+
httpInterceptor.enable();
|
80
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalledTimes(1);
|
81
|
+
});
|
82
|
+
|
83
|
+
it('should handle ignored hosts', () => {
|
84
|
+
httpInterceptor.enable({ ignoredHosts: ['example.com'] });
|
85
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
86
|
+
});
|
87
|
+
|
88
|
+
it('should handle ignored patterns', () => {
|
89
|
+
httpInterceptor.enable({ ignoredPatterns: [/^GET https:\/\/test\.com/] });
|
90
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
91
|
+
});
|
92
|
+
|
93
|
+
it('should handle ignored URLs', () => {
|
94
|
+
httpInterceptor.enable({ ignoredUrls: ['https://example.com/api'] });
|
95
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
96
|
+
});
|
97
|
+
|
98
|
+
it('should warn if another interceptor is running', () => {
|
99
|
+
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(true);
|
100
|
+
console.warn = jest.fn();
|
101
|
+
httpInterceptor.enable();
|
102
|
+
expect(console.warn).toHaveBeenCalled();
|
103
|
+
});
|
104
|
+
|
105
|
+
it('should force enable if specified', () => {
|
106
|
+
(XHRInterceptor.isInterceptorEnabled as jest.Mock).mockReturnValue(true);
|
107
|
+
httpInterceptor.enable({ forceEnable: true });
|
108
|
+
expect(XHRInterceptor.enableInterception).toHaveBeenCalled();
|
109
|
+
});
|
110
|
+
});
|
111
|
+
|
112
|
+
describe('disable', () => {
|
113
|
+
it('should disable interception', () => {
|
114
|
+
httpInterceptor.enable();
|
115
|
+
httpInterceptor.disable();
|
116
|
+
expect(XHRInterceptor.disableInterception).toHaveBeenCalled();
|
117
|
+
});
|
118
|
+
|
119
|
+
it('should not disable if not enabled', () => {
|
120
|
+
httpInterceptor.disable();
|
121
|
+
expect(XHRInterceptor.disableInterception).not.toHaveBeenCalled();
|
122
|
+
});
|
123
|
+
});
|
124
|
+
|
125
|
+
describe('listeners', () => {
|
126
|
+
it('should add and remove listeners', () => {
|
127
|
+
const listener = jest.fn();
|
128
|
+
const removeListener = httpInterceptor.addListener('open', listener);
|
129
|
+
expect(httpInterceptor['userListeners'].length).toBe(1);
|
130
|
+
removeListener();
|
131
|
+
expect(httpInterceptor['userListeners'].length).toBe(0);
|
132
|
+
});
|
133
|
+
|
134
|
+
it('should not add duplicate listeners', () => {
|
135
|
+
const listener = jest.fn();
|
136
|
+
httpInterceptor.addListener('open', listener);
|
137
|
+
httpInterceptor.addListener('open', listener);
|
138
|
+
expect(httpInterceptor['userListeners'].length).toBe(1);
|
139
|
+
});
|
140
|
+
|
141
|
+
it('should remove specific listener', () => {
|
142
|
+
const listener1 = jest.fn();
|
143
|
+
const listener2 = jest.fn();
|
144
|
+
httpInterceptor.addListener('open', listener1);
|
145
|
+
httpInterceptor.addListener('open', listener2);
|
146
|
+
httpInterceptor.removeListener('open', listener1);
|
147
|
+
expect(httpInterceptor['userListeners'].length).toBe(1);
|
148
|
+
expect(httpInterceptor['userListeners'][0][1]).toBe(listener2);
|
149
|
+
});
|
150
|
+
|
151
|
+
it('should remove all listeners', () => {
|
152
|
+
httpInterceptor.addListener('open', jest.fn());
|
153
|
+
httpInterceptor.addListener('send', jest.fn());
|
154
|
+
httpInterceptor.removeAllListener();
|
155
|
+
expect(httpInterceptor['userListeners'].length).toBe(0);
|
156
|
+
});
|
157
|
+
});
|
158
|
+
|
159
|
+
describe('request handling', () => {
|
160
|
+
let openCallback: Function;
|
161
|
+
let requestHeaderCallback: Function;
|
162
|
+
let headerReceivedCallback: Function;
|
163
|
+
let sendCallback: Function;
|
164
|
+
let responseCallback: Function;
|
165
|
+
|
166
|
+
beforeEach(() => {
|
167
|
+
httpInterceptor.enable();
|
168
|
+
openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
169
|
+
requestHeaderCallback = (XHRInterceptor.setRequestHeaderCallback as jest.Mock).mock.calls[0][0];
|
170
|
+
headerReceivedCallback = (XHRInterceptor.setHeaderReceivedCallback as jest.Mock).mock.calls[0][0];
|
171
|
+
sendCallback = (XHRInterceptor.setSendCallback as jest.Mock).mock.calls[0][0];
|
172
|
+
responseCallback = (XHRInterceptor.setResponseCallback as jest.Mock).mock.calls[0][0];
|
173
|
+
});
|
174
|
+
|
175
|
+
it('should handle open event', () => {
|
176
|
+
const listener = jest.fn();
|
177
|
+
httpInterceptor.addListener('open', listener);
|
178
|
+
const xhr = {}
|
179
|
+
openCallback('GET', 'https://example.com', xhr);
|
180
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
181
|
+
method: 'GET',
|
182
|
+
url: 'https://example.com',
|
183
|
+
}));
|
184
|
+
});
|
185
|
+
|
186
|
+
it('should handle request header event', () => {
|
187
|
+
const listener = jest.fn();
|
188
|
+
httpInterceptor.addListener('requestHeader', listener);
|
189
|
+
const xhr = {}
|
190
|
+
openCallback('GET', 'https://example.com', xhr);
|
191
|
+
requestHeaderCallback('Content-Type', 'application/json', xhr);
|
192
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
193
|
+
requestHeaders: { 'Content-Type': 'application/json' },
|
194
|
+
}));
|
195
|
+
});
|
196
|
+
|
197
|
+
it('should handle header received event', () => {
|
198
|
+
const listener = jest.fn();
|
199
|
+
httpInterceptor.addListener('headerReceived', listener);
|
200
|
+
const xhr: {[key in string]: any} = {}
|
201
|
+
openCallback('GET', 'https://example.com', xhr);
|
202
|
+
xhr.responseHeaders = { 'Content-Type': 'application/json' }
|
203
|
+
headerReceivedCallback('application/json', 100, {}, xhr);
|
204
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
205
|
+
responseContentType: 'application/json',
|
206
|
+
responseSize: 100,
|
207
|
+
responseHeaders: { 'Content-Type': 'application/json' },
|
208
|
+
}));
|
209
|
+
});
|
210
|
+
|
211
|
+
it('should handle send event with JSON data', () => {
|
212
|
+
const listener = jest.fn();
|
213
|
+
httpInterceptor.addListener('send', listener);
|
214
|
+
const xhr = {}
|
215
|
+
openCallback('POST', 'https://example.com', xhr);
|
216
|
+
sendCallback(JSON.stringify({ key: 'value' }), xhr);
|
217
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
218
|
+
requestData: { key: 'value' },
|
219
|
+
}));
|
220
|
+
});
|
221
|
+
|
222
|
+
it('should handle send event with FormData', () => {
|
223
|
+
const listener = jest.fn();
|
224
|
+
httpInterceptor.addListener('send', listener);
|
225
|
+
const xhr = {}
|
226
|
+
openCallback('POST', 'https://example.com', xhr);
|
227
|
+
const formData = new FormData();
|
228
|
+
formData.append('key', 'value');
|
229
|
+
(formDataToString as jest.Mock).mockReturnValue('key=value');
|
230
|
+
sendCallback(formData, xhr);
|
231
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
232
|
+
requestData: 'key=value',
|
233
|
+
}));
|
234
|
+
});
|
235
|
+
|
236
|
+
it('should handle response event', async () => {
|
237
|
+
const listener = jest.fn();
|
238
|
+
httpInterceptor.addListener('response', listener);
|
239
|
+
const xhr = {}
|
240
|
+
openCallback('GET', 'https://example.com', xhr);
|
241
|
+
await responseCallback(200, 1000, { data: 'response' }, 'https://example.com', 'json', xhr);
|
242
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
243
|
+
status: 200,
|
244
|
+
timeout: 1000,
|
245
|
+
responseData: { data: 'response' },
|
246
|
+
responseURL: 'https://example.com',
|
247
|
+
responseType: 'json',
|
248
|
+
}));
|
249
|
+
});
|
250
|
+
|
251
|
+
it('should handle response event with blob data', async () => {
|
252
|
+
const listener = jest.fn();
|
253
|
+
httpInterceptor.addListener('response', listener);
|
254
|
+
const xhr = {}
|
255
|
+
openCallback('GET', 'https://example.com', xhr);
|
256
|
+
const mockBlob = new MockBlob(['blob content']);
|
257
|
+
await responseCallback(200, 1000, mockBlob, 'https://example.com', 'blob', xhr);
|
258
|
+
expect(listener).toHaveBeenCalledWith(expect.objectContaining({
|
259
|
+
responseData: 'blob content',
|
260
|
+
}));
|
261
|
+
});
|
262
|
+
});
|
263
|
+
|
264
|
+
describe('error handling', () => {
|
265
|
+
beforeEach(() => {
|
266
|
+
httpInterceptor.enable();
|
267
|
+
})
|
268
|
+
it('should handle errors in listeners', async () => {
|
269
|
+
const errorListener = jest.fn(() => {
|
270
|
+
throw new Error('Listener error');
|
271
|
+
});
|
272
|
+
httpInterceptor.addListener('open', errorListener);
|
273
|
+
console.warn = jest.fn();
|
274
|
+
const xhr = {}
|
275
|
+
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
276
|
+
openCallback('GET', 'https://example.com', xhr);
|
277
|
+
|
278
|
+
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Listener error'));
|
279
|
+
});
|
280
|
+
});
|
281
|
+
|
282
|
+
describe('ignored requests', () => {
|
283
|
+
it('should ignore requests to ignored hosts', () => {
|
284
|
+
httpInterceptor.enable({ ignoredHosts: ['ignored.com'] });
|
285
|
+
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
286
|
+
const listener = jest.fn();
|
287
|
+
httpInterceptor.addListener('open', listener);
|
288
|
+
|
289
|
+
openCallback('GET', 'https://ignored.com', { uniqueId: '123' });
|
290
|
+
expect(listener).not.toHaveBeenCalled();
|
291
|
+
|
292
|
+
openCallback('GET', 'https://example.com', { uniqueId: '124' });
|
293
|
+
expect(listener).toHaveBeenCalled();
|
294
|
+
});
|
295
|
+
|
296
|
+
it('should ignore requests to ignored URLs', () => {
|
297
|
+
httpInterceptor.enable({ ignoredUrls: ['https://example.com/ignored'] });
|
298
|
+
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
299
|
+
const listener = jest.fn();
|
300
|
+
httpInterceptor.addListener('open', listener);
|
301
|
+
|
302
|
+
openCallback('GET', 'https://example.com/ignored', { uniqueId: '123' });
|
303
|
+
expect(listener).not.toHaveBeenCalled();
|
304
|
+
|
305
|
+
openCallback('GET', 'https://example.com/api', { uniqueId: '124' });
|
306
|
+
expect(listener).toHaveBeenCalled();
|
307
|
+
});
|
308
|
+
|
309
|
+
it('should ignore requests matching ignored patterns', () => {
|
310
|
+
httpInterceptor.enable({ ignoredPatterns: [/^GET https:\/\/test\.com/] });
|
311
|
+
const openCallback = (XHRInterceptor.setOpenCallback as jest.Mock).mock.calls[0][0];
|
312
|
+
const listener = jest.fn();
|
313
|
+
httpInterceptor.addListener('open', listener);
|
314
|
+
|
315
|
+
openCallback('GET', 'https://test.com/api', { uniqueId: '123' });
|
316
|
+
expect(listener).not.toHaveBeenCalled();
|
317
|
+
|
318
|
+
openCallback('POST', 'https://test.com/api', { uniqueId: '124' });
|
319
|
+
expect(listener).toHaveBeenCalled();
|
320
|
+
});
|
321
|
+
});
|
322
|
+
});
|