@react-native-harness/jest 1.1.0 → 1.2.0
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/__tests__/bridge.test.d.ts +2 -0
- package/dist/__tests__/bridge.test.d.ts.map +1 -0
- package/dist/__tests__/bridge.test.js +142 -0
- package/dist/__tests__/client-log-handler.test.js +116 -72
- package/dist/__tests__/crash-monitor.test.d.ts +2 -0
- package/dist/__tests__/crash-monitor.test.d.ts.map +1 -0
- package/dist/__tests__/crash-monitor.test.js +212 -0
- package/dist/__tests__/execute-run.test.d.ts +2 -0
- package/dist/__tests__/execute-run.test.d.ts.map +1 -0
- package/dist/__tests__/execute-run.test.js +229 -0
- package/dist/__tests__/harness.test.js +35 -0
- package/dist/__tests__/hook-queue.test.d.ts +2 -0
- package/dist/__tests__/hook-queue.test.d.ts.map +1 -0
- package/dist/__tests__/hook-queue.test.js +56 -0
- package/dist/__tests__/index.test.js +94 -37
- package/dist/__tests__/resource-lock.test.js +24 -24
- package/dist/cli-args.test.d.ts +2 -0
- package/dist/cli-args.test.d.ts.map +1 -0
- package/dist/cli-args.test.js +1 -0
- package/dist/client-log-handler.d.ts +10 -11
- package/dist/client-log-handler.d.ts.map +1 -1
- package/dist/client-log-handler.js +36 -58
- package/dist/crash-monitor.d.ts +23 -0
- package/dist/crash-monitor.d.ts.map +1 -0
- package/dist/crash-monitor.js +176 -0
- package/dist/event-mapper.d.ts +16 -0
- package/dist/event-mapper.d.ts.map +1 -0
- package/dist/event-mapper.js +120 -0
- package/dist/execute-run.d.ts +4 -0
- package/dist/execute-run.d.ts.map +1 -0
- package/dist/execute-run.js +151 -0
- package/dist/global-setup.d.ts +3 -0
- package/dist/global-setup.d.ts.map +1 -0
- package/dist/global-setup.js +60 -0
- package/dist/global-teardown.d.ts +3 -0
- package/dist/global-teardown.d.ts.map +1 -0
- package/dist/global-teardown.js +11 -0
- package/dist/harness-reporter.d.ts +8 -0
- package/dist/harness-reporter.d.ts.map +1 -0
- package/dist/harness-reporter.js +12 -0
- package/dist/harness-session.d.ts +34 -0
- package/dist/harness-session.d.ts.map +1 -0
- package/dist/harness-session.js +523 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +6 -1
- package/dist/hook-queue.d.ts +6 -0
- package/dist/hook-queue.d.ts.map +1 -0
- package/dist/hook-queue.js +18 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -230
- package/dist/jest-options.d.ts +2 -0
- package/dist/jest-options.d.ts.map +1 -0
- package/dist/jest-options.js +1 -0
- package/dist/logs.d.ts +1 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +3 -0
- package/dist/resource-lock.d.ts +0 -1
- package/dist/resource-lock.d.ts.map +1 -1
- package/dist/resource-lock.js +1 -1
- package/dist/run.d.ts +2 -2
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +3 -3
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +7 -8
- package/src/__tests__/bridge.test.ts +184 -0
- package/src/__tests__/client-log-handler.test.ts +117 -81
- package/src/__tests__/crash-monitor.test.ts +288 -0
- package/src/__tests__/execute-run.test.ts +325 -0
- package/src/__tests__/hook-queue.test.ts +77 -0
- package/src/__tests__/index.test.ts +135 -55
- package/src/__tests__/resource-lock.test.ts +29 -42
- package/src/client-log-handler.ts +48 -64
- package/src/crash-monitor.ts +248 -0
- package/src/execute-run.ts +199 -0
- package/src/harness-session.ts +734 -0
- package/src/hook-queue.ts +26 -0
- package/src/index.ts +17 -330
- package/src/logs.ts +4 -0
- package/src/resource-lock.ts +1 -1
- package/src/run.ts +5 -5
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__tests__/crash-supervisor.test.ts +0 -200
- package/src/__tests__/harness-cache.test.ts +0 -74
- package/src/__tests__/harness.test.ts +0 -870
- package/src/crash-supervisor.ts +0 -321
- package/src/env.d.ts +0 -9
- package/src/harness.ts +0 -864
- package/src/setup.ts +0 -127
- package/src/teardown.ts +0 -14
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TestContext, TestResult } from '@jest/test-result';
|
|
2
|
+
import type { Config, TestRunnerOptions } from 'jest-runner';
|
|
3
|
+
import type { CustomEnvironment } from '@react-native-harness/cli';
|
|
4
|
+
export type RunTestOptions<ExtraOptions extends Record<string, unknown> = Record<string, unknown>> = {
|
|
5
|
+
config: Config.ProjectConfig;
|
|
6
|
+
extraOptions: ExtraOptions;
|
|
7
|
+
globalConfig: Config.GlobalConfig;
|
|
8
|
+
rawModuleMap: ReturnType<TestContext['moduleMap']['getRawModuleMap']> | null;
|
|
9
|
+
options: TestRunnerOptions;
|
|
10
|
+
testPath: string;
|
|
11
|
+
};
|
|
12
|
+
export type RunTest<ExtraOptions extends Record<string, unknown> = Record<string, unknown>> = (options: RunTestOptions<ExtraOptions>, environment: CustomEnvironment) => TestResult | Promise<TestResult>;
|
|
13
|
+
export interface TestDetail {
|
|
14
|
+
title: string;
|
|
15
|
+
path: string;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,MAAM,cAAc,CACxB,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACpE;IACF,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClC,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,CACjB,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACpE,CACF,OAAO,EAAE,cAAc,CAAC,YAAY,CAAC,EACrC,WAAW,EAAE,iBAAiB,KAC3B,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-harness/jest",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -37,15 +37,14 @@
|
|
|
37
37
|
"chalk": "^4.1.2",
|
|
38
38
|
"jest-message-util": "^30.2.0",
|
|
39
39
|
"jest-util": "^30.2.0",
|
|
40
|
-
"p-limit": "^7.1.1",
|
|
41
40
|
"tslib": "^2.3.0",
|
|
42
41
|
"yargs": "^17.7.2",
|
|
43
|
-
"@react-native-harness/
|
|
44
|
-
"@react-native-harness/
|
|
45
|
-
"@react-native-harness/
|
|
46
|
-
"@react-native-harness/
|
|
47
|
-
"@react-native-harness/
|
|
48
|
-
"@react-native-harness/
|
|
42
|
+
"@react-native-harness/config": "1.2.0",
|
|
43
|
+
"@react-native-harness/platforms": "1.2.0",
|
|
44
|
+
"@react-native-harness/tools": "1.2.0",
|
|
45
|
+
"@react-native-harness/bundler-metro": "1.2.0",
|
|
46
|
+
"@react-native-harness/bridge": "1.2.0",
|
|
47
|
+
"@react-native-harness/plugins": "1.2.0"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
50
|
"@types/yargs": "^17.0.32",
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test pairing createHarnessBridge (CLI side) with
|
|
3
|
+
* connectToHarness (app side). Tests the full connection lifecycle and
|
|
4
|
+
* RPC round-trip without knowing birpc internals.
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import type { HarnessBridge } from '@react-native-harness/bridge/server';
|
|
8
|
+
import { createHarnessBridge } from '@react-native-harness/bridge/server';
|
|
9
|
+
import { connectToHarness } from '@react-native-harness/bridge/client';
|
|
10
|
+
import type { HarnessContext } from '@react-native-harness/bridge';
|
|
11
|
+
|
|
12
|
+
const makeContext = (): HarnessContext => ({
|
|
13
|
+
platform: {
|
|
14
|
+
name: 'ios',
|
|
15
|
+
platformId: 'ios',
|
|
16
|
+
runner: '/dev/null',
|
|
17
|
+
config: {},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Test setup
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
let bridge: HarnessBridge;
|
|
26
|
+
let bridgePort: number;
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
bridge = await createHarnessBridge({ port: 0, context: makeContext() });
|
|
30
|
+
bridgePort = (bridge.ws.address() as { port: number }).port;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
bridge.dispose();
|
|
35
|
+
// Allow the server to close cleanly.
|
|
36
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const connect = (callbacks: Parameters<typeof connectToHarness>[1] = { runTests: vi.fn() }) =>
|
|
40
|
+
connectToHarness(`ws://127.0.0.1:${bridgePort}`, callbacks);
|
|
41
|
+
|
|
42
|
+
const device = {
|
|
43
|
+
platform: 'ios' as const,
|
|
44
|
+
manufacturer: 'Apple',
|
|
45
|
+
model: 'iPhone 17 Pro Simulator',
|
|
46
|
+
osVersion: '18.0',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Tests
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
describe('bridge: createHarnessBridge + connectToHarness', () => {
|
|
54
|
+
describe('connection lifecycle', () => {
|
|
55
|
+
it('nextConnection() resolves once the app reports ready', async () => {
|
|
56
|
+
const connectionPromise = bridge.nextConnection();
|
|
57
|
+
|
|
58
|
+
const handle = await connect();
|
|
59
|
+
handle.reportReady(device);
|
|
60
|
+
|
|
61
|
+
const conn = await connectionPromise;
|
|
62
|
+
expect(conn.device).toEqual(device);
|
|
63
|
+
handle.disconnect();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('bridge.connection is set after reportReady and cleared on disconnect', async () => {
|
|
67
|
+
const connectionPromise = bridge.nextConnection();
|
|
68
|
+
const handle = await connect();
|
|
69
|
+
handle.reportReady(device);
|
|
70
|
+
|
|
71
|
+
await connectionPromise;
|
|
72
|
+
expect(bridge.connection).not.toBeNull();
|
|
73
|
+
|
|
74
|
+
handle.disconnect();
|
|
75
|
+
// Allow close event to propagate.
|
|
76
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
77
|
+
expect(bridge.connection).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('nextConnection() returns immediately if app already connected', async () => {
|
|
81
|
+
// App connects before nextConnection() is called.
|
|
82
|
+
const handle = await connect();
|
|
83
|
+
handle.reportReady(device);
|
|
84
|
+
|
|
85
|
+
// Yield so the server processes the reportReady before we ask.
|
|
86
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
87
|
+
|
|
88
|
+
const conn = await bridge.nextConnection();
|
|
89
|
+
expect(conn.device.platform).toBe('ios');
|
|
90
|
+
handle.disconnect();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('emits connected / disconnected events', async () => {
|
|
94
|
+
const onConnected = vi.fn();
|
|
95
|
+
const onDisconnected = vi.fn();
|
|
96
|
+
bridge.on('connected', onConnected);
|
|
97
|
+
bridge.on('disconnected', onDisconnected);
|
|
98
|
+
|
|
99
|
+
const handle = await connect();
|
|
100
|
+
handle.reportReady(device);
|
|
101
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
102
|
+
|
|
103
|
+
expect(onConnected).toHaveBeenCalledOnce();
|
|
104
|
+
|
|
105
|
+
handle.disconnect();
|
|
106
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
107
|
+
|
|
108
|
+
expect(onDisconnected).toHaveBeenCalledOnce();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('nextConnection() rejects when signal is already aborted', async () => {
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
controller.abort();
|
|
114
|
+
|
|
115
|
+
await expect(bridge.nextConnection(controller.signal)).rejects.toMatchObject({
|
|
116
|
+
name: 'AbortError',
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('nextConnection() rejects when signal is aborted while waiting', async () => {
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const promise = bridge.nextConnection(controller.signal);
|
|
123
|
+
|
|
124
|
+
controller.abort();
|
|
125
|
+
|
|
126
|
+
await expect(promise).rejects.toMatchObject({ name: 'AbortError' });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('runTests round-trip', () => {
|
|
131
|
+
it('CLI conn.runTests() invokes the app-side runTests callback', async () => {
|
|
132
|
+
const suiteResult = {
|
|
133
|
+
name: 'suite',
|
|
134
|
+
tests: [{ name: 'passes', status: 'passed' as const, duration: 10 }],
|
|
135
|
+
suites: [],
|
|
136
|
+
status: 'passed' as const,
|
|
137
|
+
duration: 30,
|
|
138
|
+
};
|
|
139
|
+
const runTestsCb = vi.fn(async () => suiteResult);
|
|
140
|
+
|
|
141
|
+
const connectionPromise = bridge.nextConnection();
|
|
142
|
+
const handle = await connect({ runTests: runTestsCb });
|
|
143
|
+
handle.reportReady(device);
|
|
144
|
+
|
|
145
|
+
const conn = await connectionPromise;
|
|
146
|
+
const result = await conn.runTests('example.ts', {
|
|
147
|
+
runner: '/runner.js',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(runTestsCb).toHaveBeenCalledWith('example.ts', expect.objectContaining({ runner: '/runner.js' }));
|
|
151
|
+
expect(result.tests[0].name).toBe('passes');
|
|
152
|
+
handle.disconnect();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('bridge events', () => {
|
|
157
|
+
it('emitEvent on app side fires the event listener on bridge', async () => {
|
|
158
|
+
const onEvent = vi.fn();
|
|
159
|
+
bridge.on('event', onEvent);
|
|
160
|
+
|
|
161
|
+
const connectionPromise = bridge.nextConnection();
|
|
162
|
+
const handle = await connect();
|
|
163
|
+
handle.reportReady(device);
|
|
164
|
+
await connectionPromise;
|
|
165
|
+
|
|
166
|
+
handle.emitEvent({ type: 'collection-started', file: 'example.ts' });
|
|
167
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
168
|
+
|
|
169
|
+
expect(onEvent).toHaveBeenCalledWith(
|
|
170
|
+
expect.objectContaining({ type: 'collection-started', file: 'example.ts' }),
|
|
171
|
+
);
|
|
172
|
+
handle.disconnect();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('dispose', () => {
|
|
177
|
+
it('rejects pending nextConnection() waiters', async () => {
|
|
178
|
+
const pending = bridge.nextConnection();
|
|
179
|
+
bridge.dispose();
|
|
180
|
+
|
|
181
|
+
await expect(pending).rejects.toThrow('Bridge disposed');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import {
|
|
3
|
+
createClientLogCollector,
|
|
3
4
|
formatClientLogMessage,
|
|
4
|
-
|
|
5
|
-
handleClientLogEvent,
|
|
6
|
-
createClientLogListener,
|
|
5
|
+
formatClientLogEntry,
|
|
7
6
|
type ClientLogEvent,
|
|
8
7
|
} from '../client-log-handler.js';
|
|
9
8
|
|
|
10
|
-
// Mock the log function
|
|
11
|
-
vi.mock('../logs.js', () => ({
|
|
12
|
-
log: vi.fn(),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
import { log } from '../logs.js';
|
|
16
|
-
|
|
17
9
|
describe('client-log-handler', () => {
|
|
18
10
|
beforeEach(() => {
|
|
19
11
|
vi.clearAllMocks();
|
|
@@ -98,7 +90,11 @@ describe('client-log-handler', () => {
|
|
|
98
90
|
});
|
|
99
91
|
|
|
100
92
|
it('should handle multiple substitutions', () => {
|
|
101
|
-
const result = formatClientLogMessage([
|
|
93
|
+
const result = formatClientLogMessage([
|
|
94
|
+
'Hello %s, you have %d messages',
|
|
95
|
+
'Alice',
|
|
96
|
+
5,
|
|
97
|
+
]);
|
|
102
98
|
expect(result).toBe('Hello Alice, you have 5 messages');
|
|
103
99
|
});
|
|
104
100
|
|
|
@@ -120,22 +116,30 @@ describe('client-log-handler', () => {
|
|
|
120
116
|
});
|
|
121
117
|
|
|
122
118
|
it('should append extra arguments after substitution', () => {
|
|
123
|
-
const result = formatClientLogMessage([
|
|
119
|
+
const result = formatClientLogMessage([
|
|
120
|
+
'Hello %s',
|
|
121
|
+
'world',
|
|
122
|
+
'extra',
|
|
123
|
+
'args',
|
|
124
|
+
]);
|
|
124
125
|
expect(result).toBe('Hello world extra args');
|
|
125
126
|
});
|
|
126
127
|
});
|
|
127
128
|
});
|
|
128
129
|
|
|
129
|
-
describe('
|
|
130
|
+
describe('formatClientLogEntry', () => {
|
|
130
131
|
it('should format log level event', () => {
|
|
131
132
|
const event: ClientLogEvent = {
|
|
132
133
|
type: 'client_log',
|
|
133
134
|
level: 'log',
|
|
134
135
|
data: ['Test message'],
|
|
135
136
|
};
|
|
136
|
-
const result =
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
const result = formatClientLogEntry(event);
|
|
138
|
+
expect(result).toEqual({
|
|
139
|
+
message: 'Test message',
|
|
140
|
+
origin: '',
|
|
141
|
+
type: 'log',
|
|
142
|
+
});
|
|
139
143
|
});
|
|
140
144
|
|
|
141
145
|
it('should format error level event', () => {
|
|
@@ -144,8 +148,12 @@ describe('client-log-handler', () => {
|
|
|
144
148
|
level: 'error',
|
|
145
149
|
data: ['Error occurred'],
|
|
146
150
|
};
|
|
147
|
-
const result =
|
|
148
|
-
expect(result).
|
|
151
|
+
const result = formatClientLogEntry(event);
|
|
152
|
+
expect(result).toEqual({
|
|
153
|
+
message: 'Error occurred',
|
|
154
|
+
origin: '',
|
|
155
|
+
type: 'error',
|
|
156
|
+
});
|
|
149
157
|
});
|
|
150
158
|
|
|
151
159
|
it('should format warn level event', () => {
|
|
@@ -154,8 +162,12 @@ describe('client-log-handler', () => {
|
|
|
154
162
|
level: 'warn',
|
|
155
163
|
data: ['Warning message'],
|
|
156
164
|
};
|
|
157
|
-
const result =
|
|
158
|
-
expect(result).
|
|
165
|
+
const result = formatClientLogEntry(event);
|
|
166
|
+
expect(result).toEqual({
|
|
167
|
+
message: 'Warning message',
|
|
168
|
+
origin: '',
|
|
169
|
+
type: 'warn',
|
|
170
|
+
});
|
|
159
171
|
});
|
|
160
172
|
|
|
161
173
|
it('should format info level event', () => {
|
|
@@ -164,8 +176,12 @@ describe('client-log-handler', () => {
|
|
|
164
176
|
level: 'info',
|
|
165
177
|
data: ['Info message'],
|
|
166
178
|
};
|
|
167
|
-
const result =
|
|
168
|
-
expect(result).
|
|
179
|
+
const result = formatClientLogEntry(event);
|
|
180
|
+
expect(result).toEqual({
|
|
181
|
+
message: 'Info message',
|
|
182
|
+
origin: '',
|
|
183
|
+
type: 'info',
|
|
184
|
+
});
|
|
169
185
|
});
|
|
170
186
|
|
|
171
187
|
it('should format debug level event', () => {
|
|
@@ -174,8 +190,12 @@ describe('client-log-handler', () => {
|
|
|
174
190
|
level: 'debug',
|
|
175
191
|
data: ['Debug message'],
|
|
176
192
|
};
|
|
177
|
-
const result =
|
|
178
|
-
expect(result).
|
|
193
|
+
const result = formatClientLogEntry(event);
|
|
194
|
+
expect(result).toEqual({
|
|
195
|
+
message: 'Debug message',
|
|
196
|
+
origin: '',
|
|
197
|
+
type: 'debug',
|
|
198
|
+
});
|
|
179
199
|
});
|
|
180
200
|
|
|
181
201
|
it('should format trace level event', () => {
|
|
@@ -184,8 +204,12 @@ describe('client-log-handler', () => {
|
|
|
184
204
|
level: 'trace',
|
|
185
205
|
data: ['Trace message'],
|
|
186
206
|
};
|
|
187
|
-
const result =
|
|
188
|
-
expect(result).
|
|
207
|
+
const result = formatClientLogEntry(event);
|
|
208
|
+
expect(result).toEqual({
|
|
209
|
+
message: 'Trace message',
|
|
210
|
+
origin: '',
|
|
211
|
+
type: 'log',
|
|
212
|
+
});
|
|
189
213
|
});
|
|
190
214
|
|
|
191
215
|
it('should handle multiple data items', () => {
|
|
@@ -194,135 +218,147 @@ describe('client-log-handler', () => {
|
|
|
194
218
|
level: 'log',
|
|
195
219
|
data: ['User:', { id: 1, name: 'Test' }],
|
|
196
220
|
};
|
|
197
|
-
const result =
|
|
198
|
-
expect(result).toContain('User:');
|
|
199
|
-
expect(result).toContain('id');
|
|
200
|
-
expect(result).toContain('Test');
|
|
221
|
+
const result = formatClientLogEntry(event);
|
|
222
|
+
expect(result?.message).toContain('User:');
|
|
223
|
+
expect(result?.message).toContain('id');
|
|
224
|
+
expect(result?.message).toContain('Test');
|
|
201
225
|
});
|
|
202
226
|
});
|
|
203
227
|
|
|
204
|
-
describe('
|
|
205
|
-
it('
|
|
228
|
+
describe('createClientLogCollector', () => {
|
|
229
|
+
it('collects client_log events and flushes them', () => {
|
|
230
|
+
const collector = createClientLogCollector();
|
|
206
231
|
const event: ClientLogEvent = {
|
|
207
232
|
type: 'client_log',
|
|
208
233
|
level: 'log',
|
|
209
234
|
data: ['Test message'],
|
|
210
235
|
};
|
|
211
236
|
|
|
212
|
-
|
|
237
|
+
collector.handleEvent(event);
|
|
213
238
|
|
|
214
|
-
expect(
|
|
215
|
-
|
|
216
|
-
|
|
239
|
+
expect(collector.flush()).toEqual([
|
|
240
|
+
{
|
|
241
|
+
message: 'Test message',
|
|
242
|
+
origin: '',
|
|
243
|
+
type: 'log',
|
|
244
|
+
},
|
|
245
|
+
]);
|
|
217
246
|
});
|
|
218
247
|
|
|
219
|
-
it('
|
|
248
|
+
it('returns an empty array for non-client_log events', () => {
|
|
249
|
+
const collector = createClientLogCollector();
|
|
220
250
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
221
251
|
const event = { type: 'bundle_build_started' } as any;
|
|
222
252
|
|
|
223
|
-
|
|
253
|
+
collector.handleEvent(event);
|
|
224
254
|
|
|
225
|
-
expect(
|
|
226
|
-
expect(log).not.toHaveBeenCalled();
|
|
255
|
+
expect(collector.flush()).toEqual([]);
|
|
227
256
|
});
|
|
228
257
|
|
|
229
|
-
it('
|
|
258
|
+
it('collects error level logs', () => {
|
|
259
|
+
const collector = createClientLogCollector();
|
|
230
260
|
const event: ClientLogEvent = {
|
|
231
261
|
type: 'client_log',
|
|
232
262
|
level: 'error',
|
|
233
263
|
data: ['Something went wrong'],
|
|
234
264
|
};
|
|
235
265
|
|
|
236
|
-
|
|
266
|
+
collector.handleEvent(event);
|
|
237
267
|
|
|
238
|
-
expect(
|
|
239
|
-
|
|
240
|
-
|
|
268
|
+
expect(collector.flush()).toEqual([
|
|
269
|
+
{
|
|
270
|
+
message: 'Something went wrong',
|
|
271
|
+
origin: '',
|
|
272
|
+
type: 'error',
|
|
273
|
+
},
|
|
274
|
+
]);
|
|
241
275
|
});
|
|
242
276
|
|
|
243
|
-
it('
|
|
277
|
+
it('collects warn level logs', () => {
|
|
278
|
+
const collector = createClientLogCollector();
|
|
244
279
|
const event: ClientLogEvent = {
|
|
245
280
|
type: 'client_log',
|
|
246
281
|
level: 'warn',
|
|
247
282
|
data: ['Deprecation warning'],
|
|
248
283
|
};
|
|
249
284
|
|
|
250
|
-
|
|
285
|
+
collector.handleEvent(event);
|
|
251
286
|
|
|
252
|
-
expect(
|
|
253
|
-
|
|
254
|
-
|
|
287
|
+
expect(collector.flush()).toEqual([
|
|
288
|
+
{
|
|
289
|
+
message: 'Deprecation warning',
|
|
290
|
+
origin: '',
|
|
291
|
+
type: 'warn',
|
|
292
|
+
},
|
|
293
|
+
]);
|
|
255
294
|
});
|
|
256
|
-
});
|
|
257
295
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const listener = createClientLogListener();
|
|
261
|
-
expect(typeof listener).toBe('function');
|
|
262
|
-
});
|
|
296
|
+
it('flush clears the buffer', () => {
|
|
297
|
+
const collector = createClientLogCollector();
|
|
263
298
|
|
|
264
|
-
|
|
265
|
-
const listener = createClientLogListener();
|
|
266
|
-
const event: ClientLogEvent = {
|
|
299
|
+
collector.handleEvent({
|
|
267
300
|
type: 'client_log',
|
|
268
301
|
level: 'info',
|
|
269
302
|
data: ['Listener test'],
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
listener(event);
|
|
303
|
+
});
|
|
273
304
|
|
|
274
|
-
expect(
|
|
305
|
+
expect(collector.flush()).toEqual([
|
|
306
|
+
{
|
|
307
|
+
message: 'Listener test',
|
|
308
|
+
origin: '',
|
|
309
|
+
type: 'info',
|
|
310
|
+
},
|
|
311
|
+
]);
|
|
312
|
+
expect(collector.flush()).toEqual([]);
|
|
275
313
|
});
|
|
276
314
|
|
|
277
|
-
it('
|
|
278
|
-
const
|
|
315
|
+
it('ignores non-client_log events', () => {
|
|
316
|
+
const collector = createClientLogCollector();
|
|
279
317
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
280
318
|
const event = { type: 'initialize_done' } as any;
|
|
281
319
|
|
|
282
|
-
|
|
320
|
+
collector.handleEvent(event);
|
|
283
321
|
|
|
284
|
-
expect(
|
|
322
|
+
expect(collector.flush()).toEqual([]);
|
|
285
323
|
});
|
|
286
|
-
});
|
|
287
324
|
|
|
288
|
-
|
|
289
|
-
|
|
325
|
+
it('ignores group events', () => {
|
|
326
|
+
const collector = createClientLogCollector();
|
|
290
327
|
const event: ClientLogEvent = {
|
|
291
328
|
type: 'client_log',
|
|
292
329
|
level: 'group',
|
|
293
330
|
data: ['Group label'],
|
|
294
331
|
};
|
|
295
332
|
|
|
296
|
-
|
|
333
|
+
collector.handleEvent(event);
|
|
297
334
|
|
|
298
|
-
expect(
|
|
299
|
-
expect(log).not.toHaveBeenCalled();
|
|
335
|
+
expect(collector.flush()).toEqual([]);
|
|
300
336
|
});
|
|
301
337
|
|
|
302
|
-
it('
|
|
338
|
+
it('ignores groupCollapsed events', () => {
|
|
339
|
+
const collector = createClientLogCollector();
|
|
303
340
|
const event: ClientLogEvent = {
|
|
304
341
|
type: 'client_log',
|
|
305
342
|
level: 'groupCollapsed',
|
|
306
343
|
data: ['Collapsed'],
|
|
307
344
|
};
|
|
308
345
|
|
|
309
|
-
|
|
346
|
+
collector.handleEvent(event);
|
|
310
347
|
|
|
311
|
-
expect(
|
|
312
|
-
expect(log).not.toHaveBeenCalled();
|
|
348
|
+
expect(collector.flush()).toEqual([]);
|
|
313
349
|
});
|
|
314
350
|
|
|
315
|
-
it('
|
|
351
|
+
it('ignores groupEnd events', () => {
|
|
352
|
+
const collector = createClientLogCollector();
|
|
316
353
|
const event: ClientLogEvent = {
|
|
317
354
|
type: 'client_log',
|
|
318
355
|
level: 'groupEnd',
|
|
319
356
|
data: [],
|
|
320
357
|
};
|
|
321
358
|
|
|
322
|
-
|
|
359
|
+
collector.handleEvent(event);
|
|
323
360
|
|
|
324
|
-
expect(
|
|
325
|
-
expect(log).not.toHaveBeenCalled();
|
|
361
|
+
expect(collector.flush()).toEqual([]);
|
|
326
362
|
});
|
|
327
363
|
});
|
|
328
364
|
});
|