@wutiange/log-listener-plugin 2.0.1 → 2.0.2-alpha.2
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 +7 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +34 -2
- package/dist/index.esm.js +5 -0
- package/dist/index.esm.js.map +1 -0
- package/index.ts +3 -3
- package/package.json +52 -59
- package/src/HTTPInterceptor.ts +340 -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 +322 -322
- package/src/__tests__/Server.test.ts +150 -150
- package/src/__tests__/utils.test.ts +114 -114
- package/src/common.ts +57 -57
- package/src/logPlugin.ts +238 -231
- package/src/logger.ts +15 -15
- package/src/utils.ts +135 -112
- package/LICENSE +0 -201
- package/dist/console.d.ts +0 -1
- package/dist/console.js +0 -20
- package/dist/console.js.map +0 -1
- package/dist/fetch.d.ts +0 -1
- package/dist/fetch.js +0 -48
- package/dist/fetch.js.map +0 -1
- 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/__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__/console.test.d.ts +0 -1
- package/dist/src/__tests__/console.test.js +0 -29
- package/dist/src/__tests__/console.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/server.d.ts +0 -21
- package/dist/src/server.js +0 -178
- package/dist/src/server.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,114 @@
|
|
1
|
-
import logger from '../logger';
|
2
|
-
import { createClassWithErrorHandling, hasPort } from '../utils';
|
3
|
-
|
4
|
-
describe('createClassWithErrorHandling', () => {
|
5
|
-
class TestClass {
|
6
|
-
normalMethod(): string {
|
7
|
-
return 'normal';
|
8
|
-
}
|
9
|
-
|
10
|
-
errorMethod(): void {
|
11
|
-
throw new Error('Test error');
|
12
|
-
}
|
13
|
-
|
14
|
-
async asyncMethod(): Promise<string> {
|
15
|
-
return 'async';
|
16
|
-
}
|
17
|
-
|
18
|
-
async asyncErrorMethod(): Promise<void> {
|
19
|
-
throw new Error('Async test error');
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
let consoleErrorSpy: jest.SpyInstance;
|
24
|
-
|
25
|
-
beforeEach(() => {
|
26
|
-
consoleErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
|
27
|
-
});
|
28
|
-
|
29
|
-
afterEach(() => {
|
30
|
-
consoleErrorSpy.mockRestore();
|
31
|
-
});
|
32
|
-
|
33
|
-
test('should not interfere with normal methods', () => {
|
34
|
-
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
35
|
-
const instance = new EnhancedClass();
|
36
|
-
expect(instance.normalMethod()).toBe('normal');
|
37
|
-
});
|
38
|
-
|
39
|
-
test('should catch and log errors from methods', () => {
|
40
|
-
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
41
|
-
const instance = new EnhancedClass();
|
42
|
-
expect(() => instance.errorMethod()).toThrow('Test error');
|
43
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in errorMethod:', expect.any(Error));
|
44
|
-
});
|
45
|
-
|
46
|
-
test('should not interfere with async methods that resolve', async () => {
|
47
|
-
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
48
|
-
const instance = new EnhancedClass();
|
49
|
-
await expect(instance.asyncMethod()).resolves.toBe('async');
|
50
|
-
});
|
51
|
-
|
52
|
-
test('should catch and log errors from async methods that reject', async () => {
|
53
|
-
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
54
|
-
const instance = new EnhancedClass();
|
55
|
-
await expect(instance.asyncErrorMethod()).rejects.toThrow('Async test error');
|
56
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in asyncErrorMethod:', expect.any(Error));
|
57
|
-
});
|
58
|
-
|
59
|
-
test('should handle methods added after instantiation', () => {
|
60
|
-
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
61
|
-
const instance = new EnhancedClass();
|
62
|
-
(instance as any).dynamicMethod = function(): void {
|
63
|
-
throw new Error('Dynamic method error');
|
64
|
-
};
|
65
|
-
expect(() => (instance as any).dynamicMethod()).toThrow('Dynamic method error');
|
66
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in dynamicMethod:', expect.any(Error));
|
67
|
-
});
|
68
|
-
});
|
69
|
-
|
70
|
-
|
71
|
-
describe('hasPort function', () => {
|
72
|
-
test('should return true for URLs with explicit ports', () => {
|
73
|
-
expect(hasPort('http://example.com:8080')).toBe(true);
|
74
|
-
expect(hasPort('ftp://example.com:210')).toBe(true);
|
75
|
-
});
|
76
|
-
|
77
|
-
test('should return false for URLs without explicit ports', () => {
|
78
|
-
expect(hasPort('http://example.com')).toBe(false);
|
79
|
-
expect(hasPort('https://example.com')).toBe(false);
|
80
|
-
expect(hasPort('ftp://example.com')).toBe(false);
|
81
|
-
});
|
82
|
-
|
83
|
-
test('should return false for invalid URLs', () => {
|
84
|
-
expect(hasPort('not a url')).toBe(false);
|
85
|
-
expect(hasPort('http:/example.com')).toBe(false);
|
86
|
-
expect(hasPort('example.com:8080')).toBe(false);
|
87
|
-
});
|
88
|
-
|
89
|
-
test('should return false for empty input', () => {
|
90
|
-
expect(hasPort('')).toBe(false);
|
91
|
-
});
|
92
|
-
|
93
|
-
test('should return false for non-string input', () => {
|
94
|
-
expect(hasPort(null as any)).toBe(false);
|
95
|
-
expect(hasPort(undefined as any)).toBe(false);
|
96
|
-
expect(hasPort(123 as any)).toBe(false);
|
97
|
-
expect(hasPort({} as any)).toBe(false);
|
98
|
-
});
|
99
|
-
|
100
|
-
test('should handle URLs with default ports correctly', () => {
|
101
|
-
expect(hasPort('http://example.com:80')).toBe(false);
|
102
|
-
expect(hasPort('https://example.com:443')).toBe(false);
|
103
|
-
});
|
104
|
-
|
105
|
-
test('should handle URLs with IPv6 addresses', () => {
|
106
|
-
expect(hasPort('http://[2001:db8::1]:8080')).toBe(true);
|
107
|
-
expect(hasPort('https://[2001:db8::1]')).toBe(false);
|
108
|
-
});
|
109
|
-
|
110
|
-
test('should handle URLs with userinfo', () => {
|
111
|
-
expect(hasPort('http://user:pass@example.com:8080')).toBe(true);
|
112
|
-
expect(hasPort('http://user:pass@example.com')).toBe(false);
|
113
|
-
});
|
114
|
-
});
|
1
|
+
import logger from '../logger';
|
2
|
+
import { createClassWithErrorHandling, hasPort } from '../utils';
|
3
|
+
|
4
|
+
describe('createClassWithErrorHandling', () => {
|
5
|
+
class TestClass {
|
6
|
+
normalMethod(): string {
|
7
|
+
return 'normal';
|
8
|
+
}
|
9
|
+
|
10
|
+
errorMethod(): void {
|
11
|
+
throw new Error('Test error');
|
12
|
+
}
|
13
|
+
|
14
|
+
async asyncMethod(): Promise<string> {
|
15
|
+
return 'async';
|
16
|
+
}
|
17
|
+
|
18
|
+
async asyncErrorMethod(): Promise<void> {
|
19
|
+
throw new Error('Async test error');
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
let consoleErrorSpy: jest.SpyInstance;
|
24
|
+
|
25
|
+
beforeEach(() => {
|
26
|
+
consoleErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
|
27
|
+
});
|
28
|
+
|
29
|
+
afterEach(() => {
|
30
|
+
consoleErrorSpy.mockRestore();
|
31
|
+
});
|
32
|
+
|
33
|
+
test('should not interfere with normal methods', () => {
|
34
|
+
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
35
|
+
const instance = new EnhancedClass();
|
36
|
+
expect(instance.normalMethod()).toBe('normal');
|
37
|
+
});
|
38
|
+
|
39
|
+
test('should catch and log errors from methods', () => {
|
40
|
+
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
41
|
+
const instance = new EnhancedClass();
|
42
|
+
expect(() => instance.errorMethod()).toThrow('Test error');
|
43
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in errorMethod:', expect.any(Error));
|
44
|
+
});
|
45
|
+
|
46
|
+
test('should not interfere with async methods that resolve', async () => {
|
47
|
+
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
48
|
+
const instance = new EnhancedClass();
|
49
|
+
await expect(instance.asyncMethod()).resolves.toBe('async');
|
50
|
+
});
|
51
|
+
|
52
|
+
test('should catch and log errors from async methods that reject', async () => {
|
53
|
+
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
54
|
+
const instance = new EnhancedClass();
|
55
|
+
await expect(instance.asyncErrorMethod()).rejects.toThrow('Async test error');
|
56
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in asyncErrorMethod:', expect.any(Error));
|
57
|
+
});
|
58
|
+
|
59
|
+
test('should handle methods added after instantiation', () => {
|
60
|
+
const EnhancedClass = createClassWithErrorHandling(TestClass);
|
61
|
+
const instance = new EnhancedClass();
|
62
|
+
(instance as any).dynamicMethod = function(): void {
|
63
|
+
throw new Error('Dynamic method error');
|
64
|
+
};
|
65
|
+
expect(() => (instance as any).dynamicMethod()).toThrow('Dynamic method error');
|
66
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error in dynamicMethod:', expect.any(Error));
|
67
|
+
});
|
68
|
+
});
|
69
|
+
|
70
|
+
|
71
|
+
describe('hasPort function', () => {
|
72
|
+
test('should return true for URLs with explicit ports', () => {
|
73
|
+
expect(hasPort('http://example.com:8080')).toBe(true);
|
74
|
+
expect(hasPort('ftp://example.com:210')).toBe(true);
|
75
|
+
});
|
76
|
+
|
77
|
+
test('should return false for URLs without explicit ports', () => {
|
78
|
+
expect(hasPort('http://example.com')).toBe(false);
|
79
|
+
expect(hasPort('https://example.com')).toBe(false);
|
80
|
+
expect(hasPort('ftp://example.com')).toBe(false);
|
81
|
+
});
|
82
|
+
|
83
|
+
test('should return false for invalid URLs', () => {
|
84
|
+
expect(hasPort('not a url')).toBe(false);
|
85
|
+
expect(hasPort('http:/example.com')).toBe(false);
|
86
|
+
expect(hasPort('example.com:8080')).toBe(false);
|
87
|
+
});
|
88
|
+
|
89
|
+
test('should return false for empty input', () => {
|
90
|
+
expect(hasPort('')).toBe(false);
|
91
|
+
});
|
92
|
+
|
93
|
+
test('should return false for non-string input', () => {
|
94
|
+
expect(hasPort(null as any)).toBe(false);
|
95
|
+
expect(hasPort(undefined as any)).toBe(false);
|
96
|
+
expect(hasPort(123 as any)).toBe(false);
|
97
|
+
expect(hasPort({} as any)).toBe(false);
|
98
|
+
});
|
99
|
+
|
100
|
+
test('should handle URLs with default ports correctly', () => {
|
101
|
+
expect(hasPort('http://example.com:80')).toBe(false);
|
102
|
+
expect(hasPort('https://example.com:443')).toBe(false);
|
103
|
+
});
|
104
|
+
|
105
|
+
test('should handle URLs with IPv6 addresses', () => {
|
106
|
+
expect(hasPort('http://[2001:db8::1]:8080')).toBe(true);
|
107
|
+
expect(hasPort('https://[2001:db8::1]')).toBe(false);
|
108
|
+
});
|
109
|
+
|
110
|
+
test('should handle URLs with userinfo', () => {
|
111
|
+
expect(hasPort('http://user:pass@example.com:8080')).toBe(true);
|
112
|
+
expect(hasPort('http://user:pass@example.com')).toBe(false);
|
113
|
+
});
|
114
|
+
});
|
package/src/common.ts
CHANGED
@@ -1,57 +1,57 @@
|
|
1
|
-
import logger from "./logger";
|
2
|
-
|
3
|
-
export const URLS_KEY = 'log-listener-plugin-urls$$SetKey'
|
4
|
-
export const DEFAULT_TIMEOUT = 3000
|
5
|
-
export const LOG_KEY = '[@wutiange/log-listener-plugin 日志]'
|
6
|
-
export enum Level {
|
7
|
-
LOG = 'log',
|
8
|
-
WARN = 'warn',
|
9
|
-
ERROR = 'error',
|
10
|
-
}
|
11
|
-
|
12
|
-
export enum Tag {
|
13
|
-
LOG_PLUGIN_INTERNAL_ERROR = 'log-plugin-internal-error',
|
14
|
-
DEFAULT = 'default',
|
15
|
-
}
|
16
|
-
|
17
|
-
const getDefaultDeviceINfo = () => {
|
18
|
-
try {
|
19
|
-
const {Platform} = require('react-native')
|
20
|
-
return {
|
21
|
-
SystemName: Platform.OS,
|
22
|
-
Version: Platform.Version,
|
23
|
-
...Platform.constants
|
24
|
-
}
|
25
|
-
} catch (error) {
|
26
|
-
logger.warn(LOG_KEY, '这个插件只能在 react-native 中使用')
|
27
|
-
return {}
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
export const getBaseData = (): Record<string, string> => {
|
32
|
-
|
33
|
-
try {
|
34
|
-
const DeviceInfo = require("react-native-device-info")?.default;
|
35
|
-
return {
|
36
|
-
Brand: DeviceInfo.getBrand(),
|
37
|
-
Model: DeviceInfo.getModel(),
|
38
|
-
AppVersion: DeviceInfo.getVersion(),
|
39
|
-
Carrier: DeviceInfo.getCarrierSync(),
|
40
|
-
Manufacturer: DeviceInfo.getManufacturerSync(),
|
41
|
-
SystemName: DeviceInfo.getSystemName(),
|
42
|
-
...getDefaultDeviceINfo()
|
43
|
-
};
|
44
|
-
} catch (error) {
|
45
|
-
return getDefaultDeviceINfo()
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
export const getDefaultStorage = (): Storage => {
|
50
|
-
try {
|
51
|
-
const AsyncStorage = require("@react-native-async-storage/async-storage")?.default;
|
52
|
-
return AsyncStorage;
|
53
|
-
} catch (error) {
|
54
|
-
return null;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
1
|
+
import logger from "./logger";
|
2
|
+
|
3
|
+
export const URLS_KEY = 'log-listener-plugin-urls$$SetKey'
|
4
|
+
export const DEFAULT_TIMEOUT = 3000
|
5
|
+
export const LOG_KEY = '[@wutiange/log-listener-plugin 日志]'
|
6
|
+
export enum Level {
|
7
|
+
LOG = 'log',
|
8
|
+
WARN = 'warn',
|
9
|
+
ERROR = 'error',
|
10
|
+
}
|
11
|
+
|
12
|
+
export enum Tag {
|
13
|
+
LOG_PLUGIN_INTERNAL_ERROR = 'log-plugin-internal-error',
|
14
|
+
DEFAULT = 'default',
|
15
|
+
}
|
16
|
+
|
17
|
+
const getDefaultDeviceINfo = () => {
|
18
|
+
try {
|
19
|
+
const {Platform} = require('react-native')
|
20
|
+
return {
|
21
|
+
SystemName: Platform.OS,
|
22
|
+
Version: Platform.Version,
|
23
|
+
...Platform.constants
|
24
|
+
}
|
25
|
+
} catch (error) {
|
26
|
+
logger.warn(LOG_KEY, '这个插件只能在 react-native 中使用')
|
27
|
+
return {}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
export const getBaseData = (): Record<string, string> => {
|
32
|
+
|
33
|
+
try {
|
34
|
+
const DeviceInfo = require("react-native-device-info")?.default;
|
35
|
+
return {
|
36
|
+
Brand: DeviceInfo.getBrand(),
|
37
|
+
Model: DeviceInfo.getModel(),
|
38
|
+
AppVersion: DeviceInfo.getVersion(),
|
39
|
+
Carrier: DeviceInfo.getCarrierSync(),
|
40
|
+
Manufacturer: DeviceInfo.getManufacturerSync(),
|
41
|
+
SystemName: DeviceInfo.getSystemName(),
|
42
|
+
...getDefaultDeviceINfo()
|
43
|
+
};
|
44
|
+
} catch (error) {
|
45
|
+
return getDefaultDeviceINfo()
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
export const getDefaultStorage = (): Storage | null => {
|
50
|
+
try {
|
51
|
+
const AsyncStorage = require("@react-native-async-storage/async-storage")?.default;
|
52
|
+
return AsyncStorage;
|
53
|
+
} catch (error) {
|
54
|
+
return null;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|