@react-native-harness/jest 1.4.0-rc.1 → 1.4.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.
Files changed (98) hide show
  1. package/dist/crash-monitor.d.ts +7 -0
  2. package/dist/crash-monitor.d.ts.map +1 -1
  3. package/dist/crash-monitor.js +19 -0
  4. package/dist/diagnostics/derivers.d.ts +24 -0
  5. package/dist/diagnostics/derivers.d.ts.map +1 -0
  6. package/dist/diagnostics/derivers.js +146 -0
  7. package/dist/diagnostics/index.d.ts +3 -0
  8. package/dist/diagnostics/index.d.ts.map +1 -0
  9. package/dist/diagnostics/index.js +2 -0
  10. package/dist/diagnostics/report.d.ts +7 -0
  11. package/dist/diagnostics/report.d.ts.map +1 -0
  12. package/dist/diagnostics/report.js +84 -0
  13. package/dist/environment-reset.d.ts +51 -0
  14. package/dist/environment-reset.d.ts.map +1 -0
  15. package/dist/environment-reset.js +83 -0
  16. package/dist/execute-run.d.ts.map +1 -1
  17. package/dist/execute-run.js +121 -69
  18. package/dist/format-harness-error.d.ts +5 -0
  19. package/dist/format-harness-error.d.ts.map +1 -0
  20. package/dist/format-harness-error.js +43 -0
  21. package/dist/harness-session.d.ts +26 -1
  22. package/dist/harness-session.d.ts.map +1 -1
  23. package/dist/harness-session.js +260 -87
  24. package/dist/run.d.ts.map +1 -1
  25. package/dist/run.js +21 -4
  26. package/package.json +8 -7
  27. package/src/crash-monitor.ts +28 -0
  28. package/src/diagnostics/derivers.ts +170 -0
  29. package/src/diagnostics/index.ts +2 -0
  30. package/src/diagnostics/report.ts +124 -0
  31. package/src/environment-reset.ts +138 -0
  32. package/src/execute-run.ts +136 -77
  33. package/src/format-harness-error.ts +73 -0
  34. package/src/harness-session.ts +369 -131
  35. package/src/run.ts +23 -4
  36. package/tsconfig.json +3 -0
  37. package/tsconfig.lib.json +3 -0
  38. package/dist/__tests__/bridge.test.d.ts +0 -2
  39. package/dist/__tests__/bridge.test.d.ts.map +0 -1
  40. package/dist/__tests__/bridge.test.js +0 -163
  41. package/dist/__tests__/client-log-handler.test.d.ts +0 -2
  42. package/dist/__tests__/client-log-handler.test.d.ts.map +0 -1
  43. package/dist/__tests__/client-log-handler.test.js +0 -306
  44. package/dist/__tests__/crash-monitor.test.d.ts +0 -2
  45. package/dist/__tests__/crash-monitor.test.d.ts.map +0 -1
  46. package/dist/__tests__/crash-monitor.test.js +0 -166
  47. package/dist/__tests__/crash-supervisor.test.d.ts +0 -2
  48. package/dist/__tests__/crash-supervisor.test.d.ts.map +0 -1
  49. package/dist/__tests__/crash-supervisor.test.js +0 -151
  50. package/dist/__tests__/errors.test.d.ts +0 -2
  51. package/dist/__tests__/errors.test.d.ts.map +0 -1
  52. package/dist/__tests__/errors.test.js +0 -98
  53. package/dist/__tests__/execute-run.test.d.ts +0 -2
  54. package/dist/__tests__/execute-run.test.d.ts.map +0 -1
  55. package/dist/__tests__/execute-run.test.js +0 -393
  56. package/dist/__tests__/fixtures/mock-platform-runner.d.ts +0 -7
  57. package/dist/__tests__/fixtures/mock-platform-runner.d.ts.map +0 -1
  58. package/dist/__tests__/fixtures/mock-platform-runner.js +0 -6
  59. package/dist/__tests__/harness-cache.test.d.ts +0 -2
  60. package/dist/__tests__/harness-cache.test.d.ts.map +0 -1
  61. package/dist/__tests__/harness-cache.test.js +0 -51
  62. package/dist/__tests__/harness-session.test.d.ts +0 -2
  63. package/dist/__tests__/harness-session.test.d.ts.map +0 -1
  64. package/dist/__tests__/harness-session.test.js +0 -74
  65. package/dist/__tests__/harness.test.d.ts +0 -2
  66. package/dist/__tests__/harness.test.d.ts.map +0 -1
  67. package/dist/__tests__/harness.test.js +0 -636
  68. package/dist/__tests__/hook-queue.test.d.ts +0 -2
  69. package/dist/__tests__/hook-queue.test.d.ts.map +0 -1
  70. package/dist/__tests__/hook-queue.test.js +0 -56
  71. package/dist/__tests__/index.test.d.ts +0 -2
  72. package/dist/__tests__/index.test.d.ts.map +0 -1
  73. package/dist/__tests__/index.test.js +0 -148
  74. package/dist/__tests__/metro-port.test.d.ts +0 -2
  75. package/dist/__tests__/metro-port.test.d.ts.map +0 -1
  76. package/dist/__tests__/metro-port.test.js +0 -52
  77. package/dist/__tests__/resource-lock.test.d.ts +0 -2
  78. package/dist/__tests__/resource-lock.test.d.ts.map +0 -1
  79. package/dist/__tests__/resource-lock.test.js +0 -138
  80. package/dist/__tests__/test-file-platform-filter.test.d.ts +0 -2
  81. package/dist/__tests__/test-file-platform-filter.test.d.ts.map +0 -1
  82. package/dist/__tests__/test-file-platform-filter.test.js +0 -47
  83. package/dist/cli-args.test.d.ts +0 -2
  84. package/dist/cli-args.test.d.ts.map +0 -1
  85. package/dist/cli-args.test.js +0 -1
  86. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  87. package/src/__tests__/bridge.test.ts +0 -219
  88. package/src/__tests__/client-log-handler.test.ts +0 -364
  89. package/src/__tests__/crash-monitor.test.ts +0 -239
  90. package/src/__tests__/errors.test.ts +0 -157
  91. package/src/__tests__/execute-run.test.ts +0 -526
  92. package/src/__tests__/harness-session.test.ts +0 -93
  93. package/src/__tests__/hook-queue.test.ts +0 -77
  94. package/src/__tests__/index.test.ts +0 -219
  95. package/src/__tests__/metro-port.test.ts +0 -61
  96. package/src/__tests__/resource-lock.test.ts +0 -179
  97. package/src/__tests__/test-file-platform-filter.test.ts +0 -83
  98. package/tsconfig.tsbuildinfo +0 -1
@@ -1,364 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import {
3
- createClientLogCollector,
4
- formatClientLogMessage,
5
- formatClientLogEntry,
6
- type ClientLogEvent,
7
- } from '../client-log-handler.js';
8
-
9
- describe('client-log-handler', () => {
10
- beforeEach(() => {
11
- vi.clearAllMocks();
12
- });
13
-
14
- describe('formatClientLogMessage', () => {
15
- it('should format a single string', () => {
16
- const result = formatClientLogMessage(['Hello, world!']);
17
- expect(result).toBe('Hello, world!');
18
- });
19
-
20
- it('should join multiple strings with spaces', () => {
21
- const result = formatClientLogMessage(['Hello', 'world', '!']);
22
- expect(result).toBe('Hello world !');
23
- });
24
-
25
- it('should format objects', () => {
26
- const result = formatClientLogMessage([{ key: 'value' }]);
27
- // util.format uses inspect-style output for objects
28
- expect(result).toContain('key');
29
- expect(result).toContain('value');
30
- });
31
-
32
- it('should format arrays', () => {
33
- const result = formatClientLogMessage([[1, 2, 3]]);
34
- expect(result).toContain('1');
35
- expect(result).toContain('2');
36
- expect(result).toContain('3');
37
- });
38
-
39
- it('should handle mixed types', () => {
40
- const result = formatClientLogMessage([
41
- 'Message:',
42
- { count: 42 },
43
- 'items',
44
- ]);
45
- expect(result).toContain('Message:');
46
- expect(result).toContain('count');
47
- expect(result).toContain('42');
48
- expect(result).toContain('items');
49
- });
50
-
51
- it('should format numbers', () => {
52
- const result = formatClientLogMessage([123, 456]);
53
- expect(result).toBe('123 456');
54
- });
55
-
56
- it('should format booleans', () => {
57
- const result = formatClientLogMessage([true, false]);
58
- expect(result).toBe('true false');
59
- });
60
-
61
- it('should format null and undefined', () => {
62
- const result = formatClientLogMessage([null, undefined]);
63
- expect(result).toBe('null undefined');
64
- });
65
-
66
- it('should handle empty array', () => {
67
- const result = formatClientLogMessage([]);
68
- expect(result).toBe('');
69
- });
70
-
71
- describe('printf-style format specifiers', () => {
72
- it('should handle %s string substitution', () => {
73
- const result = formatClientLogMessage(['%s world', 'hello']);
74
- expect(result).toBe('hello world');
75
- });
76
-
77
- it('should handle %d integer substitution', () => {
78
- const result = formatClientLogMessage(['Count: %d', 42]);
79
- expect(result).toBe('Count: 42');
80
- });
81
-
82
- it('should handle %i integer substitution', () => {
83
- const result = formatClientLogMessage(['Value: %i', 123]);
84
- expect(result).toBe('Value: 123');
85
- });
86
-
87
- it('should handle %f float substitution', () => {
88
- const result = formatClientLogMessage(['Pi: %f', 3.14159]);
89
- expect(result).toContain('3.14159');
90
- });
91
-
92
- it('should handle multiple substitutions', () => {
93
- const result = formatClientLogMessage([
94
- 'Hello %s, you have %d messages',
95
- 'Alice',
96
- 5,
97
- ]);
98
- expect(result).toBe('Hello Alice, you have 5 messages');
99
- });
100
-
101
- it('should handle %j JSON substitution', () => {
102
- const result = formatClientLogMessage(['Data: %j', { key: 'value' }]);
103
- expect(result).toBe('Data: {"key":"value"}');
104
- });
105
-
106
- it('should handle %o object substitution', () => {
107
- const result = formatClientLogMessage(['Object: %o', { a: 1 }]);
108
- // %o produces inspect-style output, just check it contains the key
109
- expect(result).toContain('a');
110
- });
111
-
112
- it('should handle %% as literal percent when substituting', () => {
113
- // %% is only converted to % when there are substitutions
114
- const result = formatClientLogMessage(['%s is 100%% complete', 'Task']);
115
- expect(result).toBe('Task is 100% complete');
116
- });
117
-
118
- it('should append extra arguments after substitution', () => {
119
- const result = formatClientLogMessage([
120
- 'Hello %s',
121
- 'world',
122
- 'extra',
123
- 'args',
124
- ]);
125
- expect(result).toBe('Hello world extra args');
126
- });
127
- });
128
- });
129
-
130
- describe('formatClientLogEntry', () => {
131
- it('should format log level event', () => {
132
- const event: ClientLogEvent = {
133
- type: 'client_log',
134
- level: 'log',
135
- data: ['Test message'],
136
- };
137
- const result = formatClientLogEntry(event);
138
- expect(result).toEqual({
139
- message: 'Test message',
140
- origin: '',
141
- type: 'log',
142
- });
143
- });
144
-
145
- it('should format error level event', () => {
146
- const event: ClientLogEvent = {
147
- type: 'client_log',
148
- level: 'error',
149
- data: ['Error occurred'],
150
- };
151
- const result = formatClientLogEntry(event);
152
- expect(result).toEqual({
153
- message: 'Error occurred',
154
- origin: '',
155
- type: 'error',
156
- });
157
- });
158
-
159
- it('should format warn level event', () => {
160
- const event: ClientLogEvent = {
161
- type: 'client_log',
162
- level: 'warn',
163
- data: ['Warning message'],
164
- };
165
- const result = formatClientLogEntry(event);
166
- expect(result).toEqual({
167
- message: 'Warning message',
168
- origin: '',
169
- type: 'warn',
170
- });
171
- });
172
-
173
- it('should format info level event', () => {
174
- const event: ClientLogEvent = {
175
- type: 'client_log',
176
- level: 'info',
177
- data: ['Info message'],
178
- };
179
- const result = formatClientLogEntry(event);
180
- expect(result).toEqual({
181
- message: 'Info message',
182
- origin: '',
183
- type: 'info',
184
- });
185
- });
186
-
187
- it('should format debug level event', () => {
188
- const event: ClientLogEvent = {
189
- type: 'client_log',
190
- level: 'debug',
191
- data: ['Debug message'],
192
- };
193
- const result = formatClientLogEntry(event);
194
- expect(result).toEqual({
195
- message: 'Debug message',
196
- origin: '',
197
- type: 'debug',
198
- });
199
- });
200
-
201
- it('should format trace level event', () => {
202
- const event: ClientLogEvent = {
203
- type: 'client_log',
204
- level: 'trace',
205
- data: ['Trace message'],
206
- };
207
- const result = formatClientLogEntry(event);
208
- expect(result).toEqual({
209
- message: 'Trace message',
210
- origin: '',
211
- type: 'log',
212
- });
213
- });
214
-
215
- it('should handle multiple data items', () => {
216
- const event: ClientLogEvent = {
217
- type: 'client_log',
218
- level: 'log',
219
- data: ['User:', { id: 1, name: 'Test' }],
220
- };
221
- const result = formatClientLogEntry(event);
222
- expect(result?.message).toContain('User:');
223
- expect(result?.message).toContain('id');
224
- expect(result?.message).toContain('Test');
225
- });
226
- });
227
-
228
- describe('createClientLogCollector', () => {
229
- it('collects client_log events and flushes them', () => {
230
- const collector = createClientLogCollector();
231
- const event: ClientLogEvent = {
232
- type: 'client_log',
233
- level: 'log',
234
- data: ['Test message'],
235
- };
236
-
237
- collector.handleEvent(event);
238
-
239
- expect(collector.flush()).toEqual([
240
- {
241
- message: 'Test message',
242
- origin: '',
243
- type: 'log',
244
- },
245
- ]);
246
- });
247
-
248
- it('returns an empty array for non-client_log events', () => {
249
- const collector = createClientLogCollector();
250
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
- const event = { type: 'bundle_build_started' } as any;
252
-
253
- collector.handleEvent(event);
254
-
255
- expect(collector.flush()).toEqual([]);
256
- });
257
-
258
- it('collects error level logs', () => {
259
- const collector = createClientLogCollector();
260
- const event: ClientLogEvent = {
261
- type: 'client_log',
262
- level: 'error',
263
- data: ['Something went wrong'],
264
- };
265
-
266
- collector.handleEvent(event);
267
-
268
- expect(collector.flush()).toEqual([
269
- {
270
- message: 'Something went wrong',
271
- origin: '',
272
- type: 'error',
273
- },
274
- ]);
275
- });
276
-
277
- it('collects warn level logs', () => {
278
- const collector = createClientLogCollector();
279
- const event: ClientLogEvent = {
280
- type: 'client_log',
281
- level: 'warn',
282
- data: ['Deprecation warning'],
283
- };
284
-
285
- collector.handleEvent(event);
286
-
287
- expect(collector.flush()).toEqual([
288
- {
289
- message: 'Deprecation warning',
290
- origin: '',
291
- type: 'warn',
292
- },
293
- ]);
294
- });
295
-
296
- it('flush clears the buffer', () => {
297
- const collector = createClientLogCollector();
298
-
299
- collector.handleEvent({
300
- type: 'client_log',
301
- level: 'info',
302
- data: ['Listener test'],
303
- });
304
-
305
- expect(collector.flush()).toEqual([
306
- {
307
- message: 'Listener test',
308
- origin: '',
309
- type: 'info',
310
- },
311
- ]);
312
- expect(collector.flush()).toEqual([]);
313
- });
314
-
315
- it('ignores non-client_log events', () => {
316
- const collector = createClientLogCollector();
317
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
318
- const event = { type: 'initialize_done' } as any;
319
-
320
- collector.handleEvent(event);
321
-
322
- expect(collector.flush()).toEqual([]);
323
- });
324
-
325
- it('ignores group events', () => {
326
- const collector = createClientLogCollector();
327
- const event: ClientLogEvent = {
328
- type: 'client_log',
329
- level: 'group',
330
- data: ['Group label'],
331
- };
332
-
333
- collector.handleEvent(event);
334
-
335
- expect(collector.flush()).toEqual([]);
336
- });
337
-
338
- it('ignores groupCollapsed events', () => {
339
- const collector = createClientLogCollector();
340
- const event: ClientLogEvent = {
341
- type: 'client_log',
342
- level: 'groupCollapsed',
343
- data: ['Collapsed'],
344
- };
345
-
346
- collector.handleEvent(event);
347
-
348
- expect(collector.flush()).toEqual([]);
349
- });
350
-
351
- it('ignores groupEnd events', () => {
352
- const collector = createClientLogCollector();
353
- const event: ClientLogEvent = {
354
- type: 'client_log',
355
- level: 'groupEnd',
356
- data: [],
357
- };
358
-
359
- collector.handleEvent(event);
360
-
361
- expect(collector.flush()).toEqual([]);
362
- });
363
- });
364
- });
@@ -1,239 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import path from 'node:path';
3
- import type {
4
- AppSession,
5
- AppSessionEvent,
6
- AppSessionListener,
7
- AppSessionLog,
8
- AppSessionState,
9
- } from '@react-native-harness/platforms';
10
- import {
11
- createCrashMonitor,
12
- CrashWatchCancelledError,
13
- } from '../crash-monitor.js';
14
- import { NativeCrashError, RuntimeDisconnectError } from '../errors.js';
15
-
16
- const noop = () => undefined;
17
- const waitForClassification = () =>
18
- new Promise((resolve) => setTimeout(resolve, 1600));
19
-
20
- const createAppSessionMock = (
21
- initialState: AppSessionState = { status: 'running' },
22
- logs: AppSessionLog[] = [],
23
- getCrashDetails?: AppSession['getCrashDetails']
24
- ) => {
25
- let state = initialState;
26
- let listener: AppSessionListener | null = null;
27
-
28
- const session: AppSession = {
29
- dispose: vi.fn(async () => undefined),
30
- getState: vi.fn(async () => state),
31
- getLogs: vi.fn(() => logs),
32
- addListener: vi.fn((l: AppSessionListener) => {
33
- listener = l;
34
- }),
35
- removeListener: vi.fn((l: AppSessionListener) => {
36
- if (listener === l) listener = null;
37
- }),
38
- };
39
-
40
- if (getCrashDetails) {
41
- session.getCrashDetails = getCrashDetails;
42
- }
43
-
44
- return {
45
- session,
46
- setState: (nextState: AppSessionState) => {
47
- state = nextState;
48
- },
49
- emit: (event: AppSessionEvent) => listener?.(event),
50
- };
51
- };
52
-
53
- describe('createCrashMonitor', () => {
54
- it('starts not alive and becomes alive when an app session is attached', () => {
55
- const cm = createCrashMonitor();
56
- const { session } = createAppSessionMock();
57
-
58
- expect(cm.isAlive()).toBe(false);
59
-
60
- cm.setAppSession(session);
61
-
62
- expect(cm.isAlive()).toBe(true);
63
- });
64
-
65
- it('rejects a watch with NativeCrashError when the app session exits', async () => {
66
- const { session, emit } = createAppSessionMock({
67
- status: 'exited',
68
- occurredAt: Date.now(),
69
- reason: 'observed-exit',
70
- });
71
- const cm = createCrashMonitor({ appSession: session });
72
- const watch = cm.watch('/test/example.ts', 'execution');
73
- watch.promise.catch(noop);
74
-
75
- emit({ type: 'app_exited' });
76
-
77
- const error = await watch.promise.catch((err: NativeCrashError) => err);
78
- expect(error).toBeInstanceOf(NativeCrashError);
79
- expect(error.testFilePath).toBe('/test/example.ts');
80
- expect(error.details.phase).toBe('execution');
81
- expect(cm.isAlive()).toBe(false);
82
- });
83
-
84
- it('classifies bridge disconnect plus exited app as NativeCrashError', async () => {
85
- const { session, setState } = createAppSessionMock();
86
- const cm = createCrashMonitor({ appSession: session });
87
- const watch = cm.watch('test.ts', 'execution');
88
- watch.promise.catch(noop);
89
-
90
- cm.handleBridgeDisconnect();
91
- setState({
92
- status: 'exited',
93
- occurredAt: Date.now(),
94
- reason: 'process-gone',
95
- });
96
-
97
- await expect(watch.promise).rejects.toBeInstanceOf(NativeCrashError);
98
- });
99
-
100
- it('classifies bridge disconnect plus running app as RuntimeDisconnectError', async () => {
101
- const { session } = createAppSessionMock({ status: 'running' });
102
- const cm = createCrashMonitor({ appSession: session });
103
- const watch = cm.watch('test.ts', 'execution');
104
- watch.promise.catch(noop);
105
-
106
- cm.handleBridgeDisconnect();
107
-
108
- await expect(watch.promise).rejects.toBeInstanceOf(RuntimeDisconnectError);
109
- });
110
-
111
- it('attaches matching session log evidence to native crash details', async () => {
112
- const occurredAt = Date.now();
113
- const logs = [
114
- { line: 'ordinary app log', occurredAt },
115
- { line: 'MyApp[123] fatal error: boom', occurredAt },
116
- ];
117
- const { session, setState } = createAppSessionMock(
118
- { status: 'running' },
119
- logs
120
- );
121
- const cm = createCrashMonitor({ appSession: session });
122
- const watch = cm.watch('test.ts', 'execution');
123
- watch.promise.catch(noop);
124
-
125
- cm.handleBridgeDisconnect();
126
- setState({ status: 'exited', occurredAt, reason: 'process-gone' });
127
-
128
- const error = await watch.promise.catch((err: NativeCrashError) => err);
129
- expect(error).toBeInstanceOf(NativeCrashError);
130
- expect(error.details.rawLines).toEqual(['MyApp[123] fatal error: boom']);
131
- });
132
-
133
- it('asks the app session to extract native crash details for the current test', async () => {
134
- const occurredAt = Date.now();
135
- const getCrashDetails = vi.fn(async () => ({
136
- artifactType: 'logcat' as const,
137
- artifactPath: '/tmp/.harness/crash-reports/crash-logcat.txt',
138
- processName: 'com.harnessplayground',
139
- pid: 7777,
140
- }));
141
- const { session, setState } = createAppSessionMock(
142
- { status: 'running' },
143
- [],
144
- getCrashDetails
145
- );
146
- const cm = createCrashMonitor({ appSession: session });
147
- const watch = cm.watch('/test/example.ts', 'execution');
148
- watch.promise.catch(noop);
149
-
150
- cm.handleBridgeDisconnect();
151
- setState({
152
- status: 'exited',
153
- occurredAt,
154
- pid: 7777,
155
- reason: 'process-gone',
156
- });
157
-
158
- const error = await watch.promise.catch((err: NativeCrashError) => err);
159
-
160
- expect(getCrashDetails).toHaveBeenCalledWith({
161
- occurredAt: expect.any(Number),
162
- pid: 7777,
163
- processName: undefined,
164
- testFilePath: '/test/example.ts',
165
- });
166
- expect(error.details.artifactPath).toBe(
167
- '/tmp/.harness/crash-reports/crash-logcat.txt'
168
- );
169
- expect(error.message).toContain(
170
- `Harness extracted the crash log: ${path.relative(
171
- process.cwd(),
172
- '/tmp/.harness/crash-reports/crash-logcat.txt'
173
- )}`
174
- );
175
- });
176
-
177
- it('falls back to log details when native crash extraction fails', async () => {
178
- const occurredAt = Date.now();
179
- const getCrashDetails = vi.fn(async () => {
180
- throw new Error('copy failed');
181
- });
182
- const { session, setState } = createAppSessionMock(
183
- { status: 'running' },
184
- [{ line: 'MyApp[123] fatal error: boom', occurredAt }],
185
- getCrashDetails
186
- );
187
- const cm = createCrashMonitor({ appSession: session });
188
- const watch = cm.watch('/test/example.ts', 'execution');
189
- watch.promise.catch(noop);
190
-
191
- cm.handleBridgeDisconnect();
192
- setState({ status: 'exited', occurredAt, reason: 'process-gone' });
193
-
194
- const error = await watch.promise.catch((err: NativeCrashError) => err);
195
-
196
- expect(error.details.rawLines).toEqual(['MyApp[123] fatal error: boom']);
197
- expect(error.message).toContain("Harness couldn't extract the crash log.");
198
- });
199
-
200
- it('settles the promise with CrashWatchCancelledError on cancel()', async () => {
201
- const cm = createCrashMonitor();
202
- const watch = cm.watch('test.ts', 'execution');
203
-
204
- watch.cancel();
205
-
206
- await expect(watch.promise).rejects.toBeInstanceOf(
207
- CrashWatchCancelledError
208
- );
209
- });
210
-
211
- it('ignores session events while stopped', async () => {
212
- const { session, emit } = createAppSessionMock({
213
- status: 'exited',
214
- occurredAt: Date.now(),
215
- });
216
- const cm = createCrashMonitor({ appSession: session });
217
- await cm.stop();
218
-
219
- const watch = cm.watch('test.ts', 'execution');
220
- const settled = vi.fn();
221
- watch.promise.then(settled, settled);
222
-
223
- emit({ type: 'app_exited' });
224
- await waitForClassification();
225
-
226
- expect(settled).not.toHaveBeenCalled();
227
- watch.cancel();
228
- });
229
-
230
- it('removes the app session listener on dispose', async () => {
231
- const { session } = createAppSessionMock();
232
- const cm = createCrashMonitor({ appSession: session });
233
-
234
- await cm.dispose();
235
-
236
- expect(session.removeListener).toHaveBeenCalledOnce();
237
- expect(cm.isAlive()).toBe(false);
238
- });
239
- });