@wutiange/log-listener-plugin 2.0.2-alpha.1 → 2.0.2-alpha.3
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/README.md +166 -163
- package/dist/index.cjs.js +1450 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +34 -2
- package/dist/index.esm.js +1448 -0
- package/dist/index.esm.js.map +1 -0
- package/index.ts +3 -3
- package/package.json +52 -59
- package/src/HTTPInterceptor.ts +336 -340
- package/src/Server.ts +174 -178
- 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 +362 -322
- package/src/__tests__/Server.test.ts +150 -150
- package/src/__tests__/utils.test.ts +236 -114
- package/src/common.ts +57 -57
- package/src/logPlugin.ts +235 -231
- package/src/logger.ts +15 -15
- package/src/utils.ts +72 -112
- package/LICENSE +0 -201
- package/dist/index.js +0 -8
- package/dist/index.js.map +0 -1
- package/dist/src/HTTPInterceptor.d.ts +0 -50
- package/dist/src/HTTPInterceptor.js +0 -227
- package/dist/src/HTTPInterceptor.js.map +0 -1
- package/dist/src/Server.d.ts +0 -21
- package/dist/src/Server.js +0 -178
- package/dist/src/Server.js.map +0 -1
- package/dist/src/__mocks__/react-native/Libraries/Blob/FileReader.d.ts +0 -14
- package/dist/src/__mocks__/react-native/Libraries/Blob/FileReader.js +0 -41
- package/dist/src/__mocks__/react-native/Libraries/Blob/FileReader.js.map +0 -1
- package/dist/src/__mocks__/react-native/Libraries/Network/XHRInterceptor.d.ts +0 -17
- package/dist/src/__mocks__/react-native/Libraries/Network/XHRInterceptor.js +0 -34
- package/dist/src/__mocks__/react-native/Libraries/Network/XHRInterceptor.js.map +0 -1
- package/dist/src/__tests__/HTTPInterceptor.test.d.ts +0 -9
- package/dist/src/__tests__/HTTPInterceptor.test.js +0 -281
- package/dist/src/__tests__/HTTPInterceptor.test.js.map +0 -1
- package/dist/src/__tests__/Server.test.d.ts +0 -1
- package/dist/src/__tests__/Server.test.js +0 -116
- package/dist/src/__tests__/Server.test.js.map +0 -1
- package/dist/src/__tests__/utils.test.d.ts +0 -1
- package/dist/src/__tests__/utils.test.js +0 -112
- package/dist/src/__tests__/utils.test.js.map +0 -1
- package/dist/src/common.d.ts +0 -14
- package/dist/src/common.js +0 -54
- package/dist/src/common.js.map +0 -1
- package/dist/src/logPlugin.d.ts +0 -35
- package/dist/src/logPlugin.js +0 -188
- package/dist/src/logPlugin.js.map +0 -1
- package/dist/src/logger.d.ts +0 -6
- package/dist/src/logger.js +0 -16
- package/dist/src/logger.js.map +0 -1
- package/dist/src/utils.d.ts +0 -6
- package/dist/src/utils.js +0 -106
- package/dist/src/utils.js.map +0 -1
- package/tsconfig.json +0 -27
@@ -1,150 +1,150 @@
|
|
1
|
-
import Server from '../Server';
|
2
|
-
import { sleep } from '../utils';
|
3
|
-
|
4
|
-
// Mock dependencies
|
5
|
-
jest.mock('../utils', () => {
|
6
|
-
const actual = jest.requireActual('../utils');
|
7
|
-
return {
|
8
|
-
...actual, // 保留所有真实实现
|
9
|
-
sleep: jest.fn() // 只模拟 sleep 函数
|
10
|
-
};
|
11
|
-
});
|
12
|
-
|
13
|
-
|
14
|
-
// Mock require for react-native-zeroconf
|
15
|
-
jest.mock('react-native-zeroconf', () => undefined, { virtual: true });
|
16
|
-
|
17
|
-
// Mock fetch
|
18
|
-
global.fetch = jest.fn();
|
19
|
-
|
20
|
-
describe('Server', () => {
|
21
|
-
let server: Server;
|
22
|
-
|
23
|
-
beforeEach(() => {
|
24
|
-
// Clear all mocks before each test
|
25
|
-
jest.clearAllMocks();
|
26
|
-
(global.fetch as jest.Mock).mockReset();
|
27
|
-
});
|
28
|
-
|
29
|
-
describe('Constructor and URL Management', () => {
|
30
|
-
it('should initialize with default values', () => {
|
31
|
-
server = new Server();
|
32
|
-
expect(server.getBaseUrlArr()).toEqual(new Set([]));
|
33
|
-
});
|
34
|
-
|
35
|
-
it('should initialize with custom URL', () => {
|
36
|
-
server = new Server('localhost:8080');
|
37
|
-
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
38
|
-
});
|
39
|
-
|
40
|
-
it('should handle URLs with and without http prefix', () => {
|
41
|
-
server = new Server();
|
42
|
-
server.updateUrl('localhost:8080');
|
43
|
-
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
44
|
-
|
45
|
-
server.updateUrl('http://localhost:8080');
|
46
|
-
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
47
|
-
});
|
48
|
-
});
|
49
|
-
|
50
|
-
describe('ZeroConf Handling', () => {
|
51
|
-
it('should handle case when zeroconf is not available', () => {
|
52
|
-
server = new Server();
|
53
|
-
const mockListener = jest.fn();
|
54
|
-
server.addUrlsListener(mockListener);
|
55
|
-
|
56
|
-
// Since Zeroconf is not available, the listener should not be called
|
57
|
-
expect(mockListener).not.toHaveBeenCalled();
|
58
|
-
});
|
59
|
-
|
60
|
-
// Test with mock Zeroconf implementation
|
61
|
-
it('should handle case when zeroconf is available', () => {
|
62
|
-
// Temporarily mock require to return a mock Zeroconf implementation
|
63
|
-
const mockZeroconfInstance = {
|
64
|
-
on: jest.fn(),
|
65
|
-
scan: jest.fn()
|
66
|
-
};
|
67
|
-
|
68
|
-
jest.doMock('react-native-zeroconf', () => ({
|
69
|
-
__esModule: true,
|
70
|
-
default: jest.fn(() => mockZeroconfInstance)
|
71
|
-
}), { virtual: true });
|
72
|
-
|
73
|
-
server = new Server();
|
74
|
-
const mockListener = jest.fn();
|
75
|
-
server.addUrlsListener(mockListener);
|
76
|
-
|
77
|
-
// Verify that Zeroconf methods were not called since module is mocked as undefined
|
78
|
-
expect(mockListener).not.toHaveBeenCalled();
|
79
|
-
});
|
80
|
-
});
|
81
|
-
|
82
|
-
describe('Data Sending', () => {
|
83
|
-
beforeEach(() => {
|
84
|
-
server = new Server('localhost:8080');
|
85
|
-
(global.fetch as jest.Mock).mockImplementation(() =>
|
86
|
-
Promise.resolve({ ok: true })
|
87
|
-
);
|
88
|
-
});
|
89
|
-
|
90
|
-
it('should send log data', async () => {
|
91
|
-
const testData = { message: 'test log' };
|
92
|
-
await server.log(testData);
|
93
|
-
|
94
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
95
|
-
'http://localhost:8080/log',
|
96
|
-
expect.objectContaining({
|
97
|
-
method: 'POST',
|
98
|
-
headers: {
|
99
|
-
'Content-Type': 'application/json;charset=utf-8'
|
100
|
-
},
|
101
|
-
body: expect.any(String)
|
102
|
-
})
|
103
|
-
);
|
104
|
-
});
|
105
|
-
|
106
|
-
it('should send network data', async () => {
|
107
|
-
const testData = { url: 'test.com' };
|
108
|
-
await server.network(testData);
|
109
|
-
|
110
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
111
|
-
'http://localhost:8080/network',
|
112
|
-
expect.objectContaining({
|
113
|
-
method: 'POST',
|
114
|
-
headers: {
|
115
|
-
'Content-Type': 'application/json;charset=utf-8'
|
116
|
-
},
|
117
|
-
body: expect.any(String)
|
118
|
-
})
|
119
|
-
);
|
120
|
-
});
|
121
|
-
|
122
|
-
it('should handle timeout', async () => {
|
123
|
-
server.updateTimeout(100);
|
124
|
-
(sleep as jest.Mock).mockImplementation(() => Promise.reject(new Error('Timeout')));
|
125
|
-
|
126
|
-
const testData = { message: 'test' };
|
127
|
-
await server.log(testData);
|
128
|
-
|
129
|
-
expect(global.fetch).toHaveBeenCalled();
|
130
|
-
expect(sleep).toHaveBeenCalledWith(100, true);
|
131
|
-
});
|
132
|
-
});
|
133
|
-
|
134
|
-
describe('Base Data Management', () => {
|
135
|
-
it('should update base data', async () => {
|
136
|
-
server = new Server('localhost:8080');
|
137
|
-
const baseData = { userId: '123' };
|
138
|
-
server.updateBaseData(baseData);
|
139
|
-
|
140
|
-
await server.log({ message: 'test' });
|
141
|
-
|
142
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
143
|
-
expect.any(String),
|
144
|
-
expect.objectContaining({
|
145
|
-
body: expect.stringContaining('"userId":"123"')
|
146
|
-
})
|
147
|
-
);
|
148
|
-
});
|
149
|
-
});
|
150
|
-
});
|
1
|
+
import Server from '../Server';
|
2
|
+
import { sleep } from '../utils';
|
3
|
+
|
4
|
+
// Mock dependencies
|
5
|
+
jest.mock('../utils', () => {
|
6
|
+
const actual = jest.requireActual('../utils');
|
7
|
+
return {
|
8
|
+
...actual, // 保留所有真实实现
|
9
|
+
sleep: jest.fn() // 只模拟 sleep 函数
|
10
|
+
};
|
11
|
+
});
|
12
|
+
|
13
|
+
|
14
|
+
// Mock require for react-native-zeroconf
|
15
|
+
jest.mock('react-native-zeroconf', () => undefined, { virtual: true });
|
16
|
+
|
17
|
+
// Mock fetch
|
18
|
+
global.fetch = jest.fn();
|
19
|
+
|
20
|
+
describe('Server', () => {
|
21
|
+
let server: Server;
|
22
|
+
|
23
|
+
beforeEach(() => {
|
24
|
+
// Clear all mocks before each test
|
25
|
+
jest.clearAllMocks();
|
26
|
+
(global.fetch as jest.Mock).mockReset();
|
27
|
+
});
|
28
|
+
|
29
|
+
describe('Constructor and URL Management', () => {
|
30
|
+
it('should initialize with default values', () => {
|
31
|
+
server = new Server();
|
32
|
+
expect(server.getBaseUrlArr()).toEqual(new Set([]));
|
33
|
+
});
|
34
|
+
|
35
|
+
it('should initialize with custom URL', () => {
|
36
|
+
server = new Server('localhost:8080');
|
37
|
+
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
38
|
+
});
|
39
|
+
|
40
|
+
it('should handle URLs with and without http prefix', () => {
|
41
|
+
server = new Server();
|
42
|
+
server.updateUrl('localhost:8080');
|
43
|
+
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
44
|
+
|
45
|
+
server.updateUrl('http://localhost:8080');
|
46
|
+
expect(server.getBaseUrlArr()).toEqual(new Set(['http://localhost:8080']));
|
47
|
+
});
|
48
|
+
});
|
49
|
+
|
50
|
+
describe('ZeroConf Handling', () => {
|
51
|
+
it('should handle case when zeroconf is not available', () => {
|
52
|
+
server = new Server();
|
53
|
+
const mockListener = jest.fn();
|
54
|
+
server.addUrlsListener(mockListener);
|
55
|
+
|
56
|
+
// Since Zeroconf is not available, the listener should not be called
|
57
|
+
expect(mockListener).not.toHaveBeenCalled();
|
58
|
+
});
|
59
|
+
|
60
|
+
// Test with mock Zeroconf implementation
|
61
|
+
it('should handle case when zeroconf is available', () => {
|
62
|
+
// Temporarily mock require to return a mock Zeroconf implementation
|
63
|
+
const mockZeroconfInstance = {
|
64
|
+
on: jest.fn(),
|
65
|
+
scan: jest.fn()
|
66
|
+
};
|
67
|
+
|
68
|
+
jest.doMock('react-native-zeroconf', () => ({
|
69
|
+
__esModule: true,
|
70
|
+
default: jest.fn(() => mockZeroconfInstance)
|
71
|
+
}), { virtual: true });
|
72
|
+
|
73
|
+
server = new Server();
|
74
|
+
const mockListener = jest.fn();
|
75
|
+
server.addUrlsListener(mockListener);
|
76
|
+
|
77
|
+
// Verify that Zeroconf methods were not called since module is mocked as undefined
|
78
|
+
expect(mockListener).not.toHaveBeenCalled();
|
79
|
+
});
|
80
|
+
});
|
81
|
+
|
82
|
+
describe('Data Sending', () => {
|
83
|
+
beforeEach(() => {
|
84
|
+
server = new Server('localhost:8080');
|
85
|
+
(global.fetch as jest.Mock).mockImplementation(() =>
|
86
|
+
Promise.resolve({ ok: true })
|
87
|
+
);
|
88
|
+
});
|
89
|
+
|
90
|
+
it('should send log data', async () => {
|
91
|
+
const testData = { message: 'test log' };
|
92
|
+
await server.log(testData);
|
93
|
+
|
94
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
95
|
+
'http://localhost:8080/log',
|
96
|
+
expect.objectContaining({
|
97
|
+
method: 'POST',
|
98
|
+
headers: {
|
99
|
+
'Content-Type': 'application/json;charset=utf-8'
|
100
|
+
},
|
101
|
+
body: expect.any(String)
|
102
|
+
})
|
103
|
+
);
|
104
|
+
});
|
105
|
+
|
106
|
+
it('should send network data', async () => {
|
107
|
+
const testData = { url: 'test.com' };
|
108
|
+
await server.network(testData);
|
109
|
+
|
110
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
111
|
+
'http://localhost:8080/network',
|
112
|
+
expect.objectContaining({
|
113
|
+
method: 'POST',
|
114
|
+
headers: {
|
115
|
+
'Content-Type': 'application/json;charset=utf-8'
|
116
|
+
},
|
117
|
+
body: expect.any(String)
|
118
|
+
})
|
119
|
+
);
|
120
|
+
});
|
121
|
+
|
122
|
+
it('should handle timeout', async () => {
|
123
|
+
server.updateTimeout(100);
|
124
|
+
(sleep as jest.Mock).mockImplementation(() => Promise.reject(new Error('Timeout')));
|
125
|
+
|
126
|
+
const testData = { message: 'test' };
|
127
|
+
await server.log(testData);
|
128
|
+
|
129
|
+
expect(global.fetch).toHaveBeenCalled();
|
130
|
+
expect(sleep).toHaveBeenCalledWith(100, true);
|
131
|
+
});
|
132
|
+
});
|
133
|
+
|
134
|
+
describe('Base Data Management', () => {
|
135
|
+
it('should update base data', async () => {
|
136
|
+
server = new Server('localhost:8080');
|
137
|
+
const baseData = { userId: '123' };
|
138
|
+
server.updateBaseData(baseData);
|
139
|
+
|
140
|
+
await server.log({ message: 'test' });
|
141
|
+
|
142
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
143
|
+
expect.any(String),
|
144
|
+
expect.objectContaining({
|
145
|
+
body: expect.stringContaining('"userId":"123"')
|
146
|
+
})
|
147
|
+
);
|
148
|
+
});
|
149
|
+
});
|
150
|
+
});
|
@@ -1,114 +1,236 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
expect(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
expect(
|
74
|
-
expect(
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
expect(
|
97
|
-
expect(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
1
|
+
import { hasPort, formDataToString, sleep, typeReplacer } from '../utils';
|
2
|
+
|
3
|
+
describe('hasPort function', () => {
|
4
|
+
it('should return true for URLs with explicit ports', () => {
|
5
|
+
expect(hasPort('http://example.com:8080')).toBe(true);
|
6
|
+
expect(hasPort('ftp://example.com:210')).toBe(true);
|
7
|
+
});
|
8
|
+
|
9
|
+
it('should return false for URLs without explicit ports', () => {
|
10
|
+
expect(hasPort('http://example.com')).toBe(false);
|
11
|
+
expect(hasPort('https://example.com')).toBe(false);
|
12
|
+
expect(hasPort('ftp://example.com')).toBe(false);
|
13
|
+
});
|
14
|
+
|
15
|
+
it('should return false for invalid URLs', () => {
|
16
|
+
expect(hasPort('not a url')).toBe(false);
|
17
|
+
expect(hasPort('http:/example.com')).toBe(false);
|
18
|
+
expect(hasPort('example.com:8080')).toBe(false);
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should return false for empty input', () => {
|
22
|
+
expect(hasPort('')).toBe(false);
|
23
|
+
});
|
24
|
+
|
25
|
+
it('should return false for non-string input', () => {
|
26
|
+
expect(hasPort(null as any)).toBe(false);
|
27
|
+
expect(hasPort(undefined as any)).toBe(false);
|
28
|
+
expect(hasPort(123 as any)).toBe(false);
|
29
|
+
expect(hasPort({} as any)).toBe(false);
|
30
|
+
});
|
31
|
+
|
32
|
+
it('should handle URLs with default ports correctly', () => {
|
33
|
+
expect(hasPort('http://example.com:80')).toBe(false);
|
34
|
+
expect(hasPort('https://example.com:443')).toBe(false);
|
35
|
+
});
|
36
|
+
|
37
|
+
it('should handle URLs with IPv6 addresses', () => {
|
38
|
+
expect(hasPort('http://[2001:db8::1]:8080')).toBe(true);
|
39
|
+
expect(hasPort('https://[2001:db8::1]')).toBe(false);
|
40
|
+
});
|
41
|
+
|
42
|
+
it('should handle URLs with userinfo', () => {
|
43
|
+
expect(hasPort('http://user:pass@example.com:8080')).toBe(true);
|
44
|
+
expect(hasPort('http://user:pass@example.com')).toBe(false);
|
45
|
+
});
|
46
|
+
});
|
47
|
+
|
48
|
+
describe('formDataToString', () => {
|
49
|
+
let mockFormData: FormData;
|
50
|
+
|
51
|
+
beforeEach(() => {
|
52
|
+
// 创建一个模拟的 FormData 对象
|
53
|
+
mockFormData = new FormData();
|
54
|
+
// 模拟 getParts 方法
|
55
|
+
(mockFormData as any).getParts = jest.fn();
|
56
|
+
});
|
57
|
+
|
58
|
+
it('should convert form data with text fields to string', () => {
|
59
|
+
// 模拟 getParts 返回包含文本字段的数据
|
60
|
+
(mockFormData as any).getParts.mockReturnValue([
|
61
|
+
{
|
62
|
+
headers: {
|
63
|
+
'content-disposition': 'form-data; name="field1"',
|
64
|
+
},
|
65
|
+
string: 'value1',
|
66
|
+
},
|
67
|
+
]);
|
68
|
+
|
69
|
+
const result = formDataToString(mockFormData);
|
70
|
+
|
71
|
+
// 验证基本结构
|
72
|
+
expect(result).toMatch(/^------WebKitFormBoundary.*\r\n/);
|
73
|
+
expect(result).toMatch(/Content-Disposition: form-data; name="field1"\r\n/);
|
74
|
+
expect(result).toMatch(/Content-Length: 6\r\n/);
|
75
|
+
expect(result).toMatch(/value1\r\n/);
|
76
|
+
expect(result).toMatch(/----WebKitFormBoundary.*--\r\n$/);
|
77
|
+
});
|
78
|
+
|
79
|
+
it('should handle form data with content-type header', () => {
|
80
|
+
// 模拟 getParts 返回包含 content-type 的数据
|
81
|
+
(mockFormData as any).getParts.mockReturnValue([
|
82
|
+
{
|
83
|
+
headers: {
|
84
|
+
'content-disposition': 'form-data; name="file"; filename="test.txt"',
|
85
|
+
'content-type': 'text/plain',
|
86
|
+
},
|
87
|
+
string: 'file content',
|
88
|
+
},
|
89
|
+
]);
|
90
|
+
|
91
|
+
const result = formDataToString(mockFormData);
|
92
|
+
|
93
|
+
expect(result).toMatch(
|
94
|
+
/Content-Disposition: form-data; name="file"; filename="test.txt"\r\n/,
|
95
|
+
);
|
96
|
+
expect(result).toMatch(/Content-Type: text\/plain\r\n/);
|
97
|
+
expect(result).toMatch(/Content-Length: 12\r\n/);
|
98
|
+
expect(result).toMatch(/file content\r\n/);
|
99
|
+
});
|
100
|
+
|
101
|
+
it('should handle multiple form fields', () => {
|
102
|
+
// 模拟 getParts 返回多个字段
|
103
|
+
(mockFormData as any).getParts.mockReturnValue([
|
104
|
+
{
|
105
|
+
headers: {
|
106
|
+
'content-disposition': 'form-data; name="field1"',
|
107
|
+
},
|
108
|
+
string: 'value1',
|
109
|
+
},
|
110
|
+
{
|
111
|
+
headers: {
|
112
|
+
'content-disposition': 'form-data; name="field2"',
|
113
|
+
},
|
114
|
+
string: 'value2',
|
115
|
+
},
|
116
|
+
]);
|
117
|
+
|
118
|
+
const result = formDataToString(mockFormData);
|
119
|
+
|
120
|
+
expect(result).toMatch(/field1.*value1.*field2.*value2/s);
|
121
|
+
expect((result.match(/----WebKitFormBoundary/g) || []).length).toBe(3); // 开始、中间、结束
|
122
|
+
});
|
123
|
+
|
124
|
+
it('should handle URI parts', () => {
|
125
|
+
// 模拟 getParts 返回包含 URI 的数据
|
126
|
+
(mockFormData as any).getParts.mockReturnValue([
|
127
|
+
{
|
128
|
+
headers: {
|
129
|
+
'content-disposition': 'form-data; name="file"',
|
130
|
+
'content-type': 'image/jpeg',
|
131
|
+
},
|
132
|
+
uri: 'file:///path/to/image.jpg',
|
133
|
+
},
|
134
|
+
]);
|
135
|
+
|
136
|
+
const result = formDataToString(mockFormData);
|
137
|
+
|
138
|
+
expect(result).toMatch(/Content-Type: image\/jpeg\r\n/);
|
139
|
+
expect(result).toMatch(/file:\/\/\/path\/to\/image.jpg\r\n/);
|
140
|
+
});
|
141
|
+
});
|
142
|
+
|
143
|
+
describe('sleep function', () => {
|
144
|
+
// 测试正常延迟情况
|
145
|
+
it('should resolve after specified delay', async () => {
|
146
|
+
const startTime = Date.now();
|
147
|
+
const delay = 100;
|
148
|
+
|
149
|
+
await sleep(delay);
|
150
|
+
const endTime = Date.now();
|
151
|
+
const actualDelay = endTime - startTime;
|
152
|
+
|
153
|
+
// 由于 JavaScript 定时器的不精确性,我们允许一个小的误差范围
|
154
|
+
expect(actualDelay).toBeGreaterThanOrEqual(delay);
|
155
|
+
expect(actualDelay).toBeLessThan(delay + 50); // 允许 50ms 的误差
|
156
|
+
});
|
157
|
+
|
158
|
+
// 测试超时拒绝情况
|
159
|
+
it('should reject with timeout error when isReject is true', async () => {
|
160
|
+
const delay = 100;
|
161
|
+
|
162
|
+
await expect(sleep(delay, true)).rejects.toEqual({
|
163
|
+
code: 11001,
|
164
|
+
key: '@wutiange/log-listener-plugin%%timeout',
|
165
|
+
message: 'Timeout',
|
166
|
+
});
|
167
|
+
});
|
168
|
+
});
|
169
|
+
|
170
|
+
describe('typeReplacer', () => {
|
171
|
+
// 测试 Error 类型转换
|
172
|
+
it('should convert Error to string', () => {
|
173
|
+
const error = new Error('test error');
|
174
|
+
expect(typeReplacer('error', error)).toBe('Error: test error');
|
175
|
+
});
|
176
|
+
|
177
|
+
// 测试 Function 类型转换
|
178
|
+
it('should convert Function to string', () => {
|
179
|
+
const fn = function test() {
|
180
|
+
return 'hello';
|
181
|
+
};
|
182
|
+
const result = typeReplacer('fn', fn);
|
183
|
+
expect(result).toContain('function test()');
|
184
|
+
});
|
185
|
+
|
186
|
+
// 测试 Symbol 类型转换
|
187
|
+
it('should convert Symbol to string', () => {
|
188
|
+
const sym = Symbol('test');
|
189
|
+
expect(typeReplacer('symbol', sym)).toBe('Symbol(test)');
|
190
|
+
});
|
191
|
+
|
192
|
+
// 测试 BigInt 类型转换
|
193
|
+
it('should convert BigInt to string', () => {
|
194
|
+
const big = BigInt(9007199254740991);
|
195
|
+
expect(typeReplacer('bigint', big)).toBe('9007199254740991');
|
196
|
+
});
|
197
|
+
|
198
|
+
// 测试 RegExp 类型转换
|
199
|
+
it('should convert RegExp to string', () => {
|
200
|
+
const regex = /test/g;
|
201
|
+
expect(typeReplacer('regex', regex)).toBe('/test/g');
|
202
|
+
});
|
203
|
+
|
204
|
+
// 测试 Set 类型转换
|
205
|
+
it('should convert Set to array', () => {
|
206
|
+
const set = new Set([1, 2, 3]);
|
207
|
+
expect(typeReplacer('set', set)).toEqual([1, 2, 3]);
|
208
|
+
});
|
209
|
+
|
210
|
+
// 测试 Map 类型转换
|
211
|
+
it('should convert Map to object', () => {
|
212
|
+
const map = new Map([
|
213
|
+
['key1', 'value1'],
|
214
|
+
['key2', 'value2'],
|
215
|
+
]);
|
216
|
+
expect(typeReplacer('map', map)).toEqual({
|
217
|
+
key1: 'value1',
|
218
|
+
key2: 'value2',
|
219
|
+
});
|
220
|
+
});
|
221
|
+
|
222
|
+
// 测试普通值不变
|
223
|
+
it('should return primitive values as is', () => {
|
224
|
+
expect(typeReplacer('string', 'test')).toBe('test');
|
225
|
+
expect(typeReplacer('number', 42)).toBe(42);
|
226
|
+
expect(typeReplacer('boolean', true)).toBe(true);
|
227
|
+
expect(typeReplacer('null', null)).toBe(null);
|
228
|
+
expect(typeReplacer('undefined', undefined)).toBe(undefined);
|
229
|
+
});
|
230
|
+
|
231
|
+
// 测试普通对象不变
|
232
|
+
it('should return objects as is', () => {
|
233
|
+
const obj = { name: 'test', age: 25 };
|
234
|
+
expect(typeReplacer('object', obj)).toEqual(obj);
|
235
|
+
});
|
236
|
+
});
|