@incodetech/core 2.0.0-alpha.2 → 2.0.0-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/package.json +4 -1
- package/.turbo/turbo-build.log +0 -33
- package/.turbo/turbo-coverage.log +0 -22
- package/.turbo/turbo-format.log +0 -6
- package/.turbo/turbo-lint$colon$fix.log +0 -77
- package/.turbo/turbo-lint.log +0 -95
- package/.turbo/turbo-test.log +0 -870
- package/.turbo/turbo-typecheck.log +0 -5
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -221
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/camera/cameraService.ts.html +0 -580
- package/coverage/src/camera/cameraServices.ts.html +0 -163
- package/coverage/src/camera/cameraStateMachine.ts.html +0 -877
- package/coverage/src/camera/index.html +0 -146
- package/coverage/src/email/emailActor.ts.html +0 -130
- package/coverage/src/email/emailManager.ts.html +0 -1366
- package/coverage/src/email/emailStateMachine.ts.html +0 -1186
- package/coverage/src/email/index.html +0 -146
- package/coverage/src/flow/flowActor.ts.html +0 -124
- package/coverage/src/flow/flowAnalyzer.ts.html +0 -196
- package/coverage/src/flow/flowManager.ts.html +0 -790
- package/coverage/src/flow/flowServices.ts.html +0 -124
- package/coverage/src/flow/flowStateMachine.ts.html +0 -631
- package/coverage/src/flow/index.html +0 -221
- package/coverage/src/flow/moduleLoader.ts.html +0 -304
- package/coverage/src/flow/orchestratedFlowManager.ts.html +0 -778
- package/coverage/src/flow/orchestratedFlowStateMachine.ts.html +0 -1060
- package/coverage/src/http/api.ts.html +0 -355
- package/coverage/src/http/endpoints.ts.html +0 -136
- package/coverage/src/http/index.html +0 -131
- package/coverage/src/index.html +0 -116
- package/coverage/src/phone/index.html +0 -146
- package/coverage/src/phone/phoneActor.ts.html +0 -130
- package/coverage/src/phone/phoneManager.ts.html +0 -1459
- package/coverage/src/phone/phoneStateMachine.ts.html +0 -1351
- package/coverage/src/recordings/index.html +0 -116
- package/coverage/src/recordings/recordingsRepository.ts.html +0 -229
- package/coverage/src/selfie/index.html +0 -191
- package/coverage/src/selfie/selfieActor.ts.html +0 -136
- package/coverage/src/selfie/selfieErrorUtils.ts.html +0 -283
- package/coverage/src/selfie/selfieManager.ts.html +0 -988
- package/coverage/src/selfie/selfieStateMachine.ts.html +0 -2497
- package/coverage/src/selfie/selfieUploadService.ts.html +0 -328
- package/coverage/src/selfie/types.ts.html +0 -394
- package/coverage/src/setup.ts.html +0 -598
- package/src/camera/cameraActor.ts +0 -21
- package/src/camera/cameraService.test.ts +0 -437
- package/src/camera/cameraService.ts +0 -165
- package/src/camera/cameraServices.test.ts +0 -66
- package/src/camera/cameraServices.ts +0 -26
- package/src/camera/cameraStateMachine.test.ts +0 -602
- package/src/camera/cameraStateMachine.ts +0 -264
- package/src/camera/index.ts +0 -5
- package/src/camera/types.ts +0 -17
- package/src/device/getBrowser.ts +0 -31
- package/src/device/getDeviceClass.ts +0 -29
- package/src/device/index.ts +0 -2
- package/src/email/__mocks__/emailMocks.ts +0 -59
- package/src/email/emailActor.ts +0 -15
- package/src/email/emailManager.test.ts +0 -573
- package/src/email/emailManager.ts +0 -427
- package/src/email/emailServices.ts +0 -66
- package/src/email/emailStateMachine.test.ts +0 -741
- package/src/email/emailStateMachine.ts +0 -367
- package/src/email/index.ts +0 -39
- package/src/email/types.ts +0 -60
- package/src/events/addEvent.ts +0 -20
- package/src/events/types.ts +0 -7
- package/src/flow/__mocks__/flowMocks.ts +0 -84
- package/src/flow/flowActor.ts +0 -13
- package/src/flow/flowAnalyzer.test.ts +0 -266
- package/src/flow/flowAnalyzer.ts +0 -37
- package/src/flow/flowCompletionService.ts +0 -21
- package/src/flow/flowManager.test.ts +0 -560
- package/src/flow/flowManager.ts +0 -235
- package/src/flow/flowServices.test.ts +0 -109
- package/src/flow/flowServices.ts +0 -13
- package/src/flow/flowStateMachine.test.ts +0 -334
- package/src/flow/flowStateMachine.ts +0 -182
- package/src/flow/index.ts +0 -21
- package/src/flow/moduleLoader.test.ts +0 -136
- package/src/flow/moduleLoader.ts +0 -73
- package/src/flow/orchestratedFlowManager.test.ts +0 -240
- package/src/flow/orchestratedFlowManager.ts +0 -231
- package/src/flow/orchestratedFlowStateMachine.test.ts +0 -199
- package/src/flow/orchestratedFlowStateMachine.ts +0 -325
- package/src/flow/types.ts +0 -434
- package/src/http/__mocks__/api.ts +0 -88
- package/src/http/api.test.ts +0 -231
- package/src/http/api.ts +0 -90
- package/src/http/endpoints.ts +0 -17
- package/src/index.ts +0 -33
- package/src/permissions/index.ts +0 -2
- package/src/permissions/permissionServices.ts +0 -31
- package/src/permissions/types.ts +0 -3
- package/src/phone/__mocks__/phoneMocks.ts +0 -71
- package/src/phone/index.ts +0 -39
- package/src/phone/phoneActor.ts +0 -15
- package/src/phone/phoneManager.test.ts +0 -393
- package/src/phone/phoneManager.ts +0 -458
- package/src/phone/phoneServices.ts +0 -98
- package/src/phone/phoneStateMachine.test.ts +0 -918
- package/src/phone/phoneStateMachine.ts +0 -422
- package/src/phone/types.ts +0 -83
- package/src/recordings/recordingsRepository.test.ts +0 -87
- package/src/recordings/recordingsRepository.ts +0 -48
- package/src/recordings/streamingEvents.ts +0 -10
- package/src/selfie/__mocks__/selfieMocks.ts +0 -26
- package/src/selfie/index.ts +0 -14
- package/src/selfie/selfieActor.ts +0 -17
- package/src/selfie/selfieErrorUtils.test.ts +0 -116
- package/src/selfie/selfieErrorUtils.ts +0 -66
- package/src/selfie/selfieManager.test.ts +0 -297
- package/src/selfie/selfieManager.ts +0 -301
- package/src/selfie/selfieServices.ts +0 -362
- package/src/selfie/selfieStateMachine.test.ts +0 -283
- package/src/selfie/selfieStateMachine.ts +0 -804
- package/src/selfie/selfieUploadService.test.ts +0 -90
- package/src/selfie/selfieUploadService.ts +0 -81
- package/src/selfie/types.ts +0 -103
- package/src/session/index.ts +0 -5
- package/src/session/sessionService.ts +0 -78
- package/src/setup.test.ts +0 -61
- package/src/setup.ts +0 -171
- package/tsconfig.json +0 -13
- package/tsdown.config.ts +0 -22
- package/vitest.config.ts +0 -37
- package/vitest.setup.ts +0 -135
|
@@ -1,602 +0,0 @@
|
|
|
1
|
-
import { createActor, MockCameraProvider } from '@incodetech/infra';
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { cameraMachine } from './cameraStateMachine';
|
|
4
|
-
|
|
5
|
-
const createTestActor = () => {
|
|
6
|
-
const camera = new MockCameraProvider();
|
|
7
|
-
return createActor(cameraMachine, {
|
|
8
|
-
input: { camera },
|
|
9
|
-
}).start();
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// Helper to mock permission query result
|
|
13
|
-
const mockPermissionQuery = (state: 'granted' | 'denied' | 'prompt') => {
|
|
14
|
-
const setPermissionState = (
|
|
15
|
-
globalThis as {
|
|
16
|
-
__setPermissionState?: (state: 'granted' | 'denied' | 'prompt') => void;
|
|
17
|
-
}
|
|
18
|
-
).__setPermissionState;
|
|
19
|
-
if (setPermissionState) {
|
|
20
|
-
setPermissionState(state);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
describe('CameraStateMachine', () => {
|
|
25
|
-
describe('Initial state', () => {
|
|
26
|
-
it('should start in idle state', () => {
|
|
27
|
-
const actor = createTestActor();
|
|
28
|
-
|
|
29
|
-
expect(actor.getSnapshot().value).toBe('idle');
|
|
30
|
-
actor.stop();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should have initial context with camera capability', () => {
|
|
34
|
-
const actor = createTestActor();
|
|
35
|
-
|
|
36
|
-
const { context } = actor.getSnapshot();
|
|
37
|
-
expect(context.camera).toBeDefined();
|
|
38
|
-
expect(context.stream).toBeUndefined();
|
|
39
|
-
expect(context.permissionResult).toBeUndefined();
|
|
40
|
-
expect(context.error).toBeUndefined();
|
|
41
|
-
expect(context.errorType).toBeUndefined();
|
|
42
|
-
|
|
43
|
-
actor.stop();
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('START event', () => {
|
|
48
|
-
it('should transition from idle to checkingPermission', () => {
|
|
49
|
-
const actor = createTestActor();
|
|
50
|
-
|
|
51
|
-
actor.send({ type: 'START' });
|
|
52
|
-
|
|
53
|
-
expect(actor.getSnapshot().value).toBe('checkingPermission');
|
|
54
|
-
actor.stop();
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('Permission checking flow', () => {
|
|
59
|
-
it('should transition to routing after permission check', async () => {
|
|
60
|
-
const actor = createTestActor();
|
|
61
|
-
|
|
62
|
-
actor.send({ type: 'START' });
|
|
63
|
-
|
|
64
|
-
await vi.waitFor(
|
|
65
|
-
() => {
|
|
66
|
-
const snapshot = actor.getSnapshot();
|
|
67
|
-
expect(['routing', 'requesting', 'streaming']).toContain(
|
|
68
|
-
snapshot.value,
|
|
69
|
-
);
|
|
70
|
-
},
|
|
71
|
-
{ timeout: 2000 },
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
actor.stop();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should set permission result in context', async () => {
|
|
78
|
-
const actor = createTestActor();
|
|
79
|
-
|
|
80
|
-
actor.send({ type: 'START' });
|
|
81
|
-
|
|
82
|
-
await vi.waitFor(
|
|
83
|
-
() => {
|
|
84
|
-
const snapshot = actor.getSnapshot();
|
|
85
|
-
if (
|
|
86
|
-
snapshot.value === 'routing' ||
|
|
87
|
-
snapshot.value === 'requesting' ||
|
|
88
|
-
snapshot.value === 'streaming'
|
|
89
|
-
) {
|
|
90
|
-
expect(snapshot.context.permissionResult).toBeDefined();
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
{ timeout: 2000 },
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
actor.stop();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('Stream request flow', () => {
|
|
101
|
-
it('should transition to streaming on successful stream', async () => {
|
|
102
|
-
const camera = new MockCameraProvider();
|
|
103
|
-
const mockStream = new MediaStream();
|
|
104
|
-
camera.setMockStream(mockStream);
|
|
105
|
-
const actor = createActor(cameraMachine, {
|
|
106
|
-
input: { camera },
|
|
107
|
-
}).start();
|
|
108
|
-
|
|
109
|
-
actor.send({ type: 'START' });
|
|
110
|
-
|
|
111
|
-
await vi.waitFor(
|
|
112
|
-
() => {
|
|
113
|
-
const value = actor.getSnapshot().value;
|
|
114
|
-
if (value !== 'streaming' && value !== 'error') {
|
|
115
|
-
throw new Error(`Still in state: ${value}`);
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
{ timeout: 5000, interval: 50 },
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
const snapshot = actor.getSnapshot();
|
|
122
|
-
expect(snapshot.value).toBe('streaming');
|
|
123
|
-
expect(snapshot.context.stream).toBe(mockStream);
|
|
124
|
-
|
|
125
|
-
actor.stop();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should transition to error on stream failure', async () => {
|
|
129
|
-
const camera = new MockCameraProvider();
|
|
130
|
-
camera.setShouldReject(true);
|
|
131
|
-
const actor = createActor(cameraMachine, {
|
|
132
|
-
input: { camera },
|
|
133
|
-
}).start();
|
|
134
|
-
|
|
135
|
-
actor.send({ type: 'START' });
|
|
136
|
-
|
|
137
|
-
await vi.waitFor(
|
|
138
|
-
() => {
|
|
139
|
-
const value = actor.getSnapshot().value;
|
|
140
|
-
if (value !== 'error' && value !== 'streaming') {
|
|
141
|
-
throw new Error(`Still in state: ${value}`);
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
{ timeout: 5000, interval: 50 },
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const snapshot = actor.getSnapshot();
|
|
148
|
-
expect(snapshot.value).toBe('error');
|
|
149
|
-
expect(snapshot.context.error).toBeDefined();
|
|
150
|
-
expect(snapshot.context.errorType).toBeDefined();
|
|
151
|
-
|
|
152
|
-
actor.stop();
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('STOP event', () => {
|
|
157
|
-
it('should transition from checkingPermission to idle', () => {
|
|
158
|
-
const actor = createTestActor();
|
|
159
|
-
|
|
160
|
-
actor.send({ type: 'START' });
|
|
161
|
-
actor.send({ type: 'STOP' });
|
|
162
|
-
|
|
163
|
-
expect(actor.getSnapshot().value).toBe('idle');
|
|
164
|
-
actor.stop();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should transition from requesting to idle', () => {
|
|
168
|
-
const actor = createTestActor();
|
|
169
|
-
|
|
170
|
-
actor.send({ type: 'START' });
|
|
171
|
-
actor.send({ type: 'STOP' });
|
|
172
|
-
|
|
173
|
-
expect(actor.getSnapshot().value).toBe('idle');
|
|
174
|
-
actor.stop();
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('Routing state', () => {
|
|
179
|
-
beforeEach(() => {
|
|
180
|
-
// Reset permission mock to default
|
|
181
|
-
mockPermissionQuery('prompt');
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('should route to requesting when permission is granted', async () => {
|
|
185
|
-
// Set mock BEFORE creating actor
|
|
186
|
-
mockPermissionQuery('granted');
|
|
187
|
-
const camera = new MockCameraProvider();
|
|
188
|
-
const mockStream = new MediaStream();
|
|
189
|
-
camera.setMockStream(mockStream);
|
|
190
|
-
const actor = createActor(cameraMachine, {
|
|
191
|
-
input: { camera },
|
|
192
|
-
}).start();
|
|
193
|
-
|
|
194
|
-
actor.send({ type: 'START' });
|
|
195
|
-
|
|
196
|
-
// Wait for streaming state (granted -> routing -> requesting -> streaming)
|
|
197
|
-
await vi.waitFor(
|
|
198
|
-
() => {
|
|
199
|
-
const value = actor.getSnapshot().value;
|
|
200
|
-
if (value !== 'streaming' && value !== 'error') {
|
|
201
|
-
throw new Error(`Still in state: ${value}`);
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
{ timeout: 5000, interval: 50 },
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
expect(actor.getSnapshot().value).toBe('streaming');
|
|
208
|
-
expect(actor.getSnapshot().context.permissionResult).toBe('granted');
|
|
209
|
-
|
|
210
|
-
actor.stop();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should route to error when permission is denied', async () => {
|
|
214
|
-
// Set mock BEFORE creating actor
|
|
215
|
-
mockPermissionQuery('denied');
|
|
216
|
-
const camera = new MockCameraProvider();
|
|
217
|
-
const actor = createActor(cameraMachine, {
|
|
218
|
-
input: { camera },
|
|
219
|
-
}).start();
|
|
220
|
-
|
|
221
|
-
actor.send({ type: 'START' });
|
|
222
|
-
|
|
223
|
-
// Wait for error state (denied -> routing -> error)
|
|
224
|
-
await vi.waitFor(
|
|
225
|
-
() => {
|
|
226
|
-
const value = actor.getSnapshot().value;
|
|
227
|
-
if (value !== 'error' && value !== 'streaming') {
|
|
228
|
-
throw new Error(`Still in state: ${value}`);
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
{ timeout: 5000, interval: 50 },
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
const snapshot = actor.getSnapshot();
|
|
235
|
-
expect(snapshot.value).toBe('error');
|
|
236
|
-
expect(snapshot.context.permissionResult).toBe('denied');
|
|
237
|
-
expect(snapshot.context.errorType).toBe('permission_denied');
|
|
238
|
-
|
|
239
|
-
actor.stop();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should route to requesting when permission is prompt', async () => {
|
|
243
|
-
// Set mock BEFORE creating actor
|
|
244
|
-
mockPermissionQuery('prompt');
|
|
245
|
-
const camera = new MockCameraProvider();
|
|
246
|
-
const mockStream = new MediaStream();
|
|
247
|
-
camera.setMockStream(mockStream);
|
|
248
|
-
const actor = createActor(cameraMachine, {
|
|
249
|
-
input: { camera },
|
|
250
|
-
}).start();
|
|
251
|
-
|
|
252
|
-
actor.send({ type: 'START' });
|
|
253
|
-
|
|
254
|
-
// Wait for streaming (prompt -> routing -> requesting -> streaming)
|
|
255
|
-
await vi.waitFor(
|
|
256
|
-
() => {
|
|
257
|
-
const value = actor.getSnapshot().value;
|
|
258
|
-
if (value !== 'streaming' && value !== 'error') {
|
|
259
|
-
throw new Error(`Still in state: ${value}`);
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
{ timeout: 5000, interval: 50 },
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
expect(actor.getSnapshot().value).toBe('streaming');
|
|
266
|
-
expect(actor.getSnapshot().context.permissionResult).toBe('prompt');
|
|
267
|
-
|
|
268
|
-
actor.stop();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe('Streaming state', () => {
|
|
273
|
-
it('should transition to idle when STOP is sent from streaming', async () => {
|
|
274
|
-
const camera = new MockCameraProvider();
|
|
275
|
-
const mockStream = new MediaStream();
|
|
276
|
-
camera.setMockStream(mockStream);
|
|
277
|
-
const stopSpy = vi.spyOn(camera, 'stopStream');
|
|
278
|
-
const actor = createActor(cameraMachine, {
|
|
279
|
-
input: { camera },
|
|
280
|
-
}).start();
|
|
281
|
-
|
|
282
|
-
actor.send({ type: 'START' });
|
|
283
|
-
|
|
284
|
-
// Wait for streaming state
|
|
285
|
-
await vi.waitFor(
|
|
286
|
-
() => {
|
|
287
|
-
const snapshot = actor.getSnapshot();
|
|
288
|
-
if (snapshot.value !== 'streaming' || !snapshot.context.stream) {
|
|
289
|
-
throw new Error(`Not streaming yet: ${snapshot.value}`);
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
{ timeout: 5000, interval: 50 },
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Send STOP
|
|
296
|
-
actor.send({ type: 'STOP' });
|
|
297
|
-
|
|
298
|
-
// STOP should transition immediately (synchronous)
|
|
299
|
-
const stopSnapshot = actor.getSnapshot();
|
|
300
|
-
expect(stopSnapshot.value).toBe('idle');
|
|
301
|
-
|
|
302
|
-
// stopStream should be called because stream was in context
|
|
303
|
-
expect(stopSpy).toHaveBeenCalled();
|
|
304
|
-
|
|
305
|
-
actor.stop();
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
describe('Error state', () => {
|
|
310
|
-
it('should transition to idle when RESET is sent from error', () => {
|
|
311
|
-
const actor = createTestActor();
|
|
312
|
-
|
|
313
|
-
// Manually transition to error state
|
|
314
|
-
actor.send({ type: 'START' });
|
|
315
|
-
actor.send({
|
|
316
|
-
type: 'STREAM_ERROR',
|
|
317
|
-
error: 'Test error',
|
|
318
|
-
errorType: 'unknown',
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// Wait a tick for state update
|
|
322
|
-
const errorSnapshot = actor.getSnapshot();
|
|
323
|
-
if (errorSnapshot.value === 'error') {
|
|
324
|
-
actor.send({ type: 'RESET' });
|
|
325
|
-
expect(actor.getSnapshot().value).toBe('idle');
|
|
326
|
-
expect(actor.getSnapshot().context.error).toBeUndefined();
|
|
327
|
-
expect(actor.getSnapshot().context.errorType).toBeUndefined();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
actor.stop();
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should transition to idle when STOP is sent from error', () => {
|
|
334
|
-
const actor = createTestActor();
|
|
335
|
-
|
|
336
|
-
// Manually transition to error state
|
|
337
|
-
actor.send({ type: 'START' });
|
|
338
|
-
actor.send({
|
|
339
|
-
type: 'STREAM_ERROR',
|
|
340
|
-
error: 'Test error',
|
|
341
|
-
errorType: 'unknown',
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
// Wait a tick for state update
|
|
345
|
-
const errorSnapshot = actor.getSnapshot();
|
|
346
|
-
if (errorSnapshot.value === 'error') {
|
|
347
|
-
actor.send({ type: 'STOP' });
|
|
348
|
-
expect(actor.getSnapshot().value).toBe('idle');
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
actor.stop();
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe('Error handling', () => {
|
|
356
|
-
it('should set error type for NotAllowedError', async () => {
|
|
357
|
-
const camera = new MockCameraProvider();
|
|
358
|
-
camera.setShouldReject(
|
|
359
|
-
true,
|
|
360
|
-
new DOMException('Permission denied', 'NotAllowedError'),
|
|
361
|
-
);
|
|
362
|
-
const actor = createActor(cameraMachine, {
|
|
363
|
-
input: { camera },
|
|
364
|
-
}).start();
|
|
365
|
-
|
|
366
|
-
actor.send({ type: 'START' });
|
|
367
|
-
|
|
368
|
-
await vi.waitFor(
|
|
369
|
-
() => {
|
|
370
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
371
|
-
throw new Error('Not in error state yet');
|
|
372
|
-
}
|
|
373
|
-
},
|
|
374
|
-
{ timeout: 5000, interval: 50 },
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
const errorSnapshot = actor.getSnapshot();
|
|
378
|
-
expect(errorSnapshot.value).toBe('error');
|
|
379
|
-
expect(errorSnapshot.context.errorType).toBe('permission_denied');
|
|
380
|
-
|
|
381
|
-
actor.stop();
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('should set error type for NotFoundError', async () => {
|
|
385
|
-
const camera = new MockCameraProvider();
|
|
386
|
-
camera.setShouldReject(
|
|
387
|
-
true,
|
|
388
|
-
new DOMException('Camera not found', 'NotFoundError'),
|
|
389
|
-
);
|
|
390
|
-
const actor = createActor(cameraMachine, {
|
|
391
|
-
input: { camera },
|
|
392
|
-
}).start();
|
|
393
|
-
|
|
394
|
-
actor.send({ type: 'START' });
|
|
395
|
-
|
|
396
|
-
await vi.waitFor(
|
|
397
|
-
() => {
|
|
398
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
399
|
-
throw new Error('Not in error state yet');
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
{ timeout: 5000, interval: 50 },
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
const errorSnapshot = actor.getSnapshot();
|
|
406
|
-
expect(errorSnapshot.value).toBe('error');
|
|
407
|
-
expect(errorSnapshot.context.errorType).toBe('not_found');
|
|
408
|
-
|
|
409
|
-
actor.stop();
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it('should set error type for NotReadableError', async () => {
|
|
413
|
-
const camera = new MockCameraProvider();
|
|
414
|
-
camera.setShouldReject(
|
|
415
|
-
true,
|
|
416
|
-
new DOMException('Camera in use', 'NotReadableError'),
|
|
417
|
-
);
|
|
418
|
-
const actor = createActor(cameraMachine, {
|
|
419
|
-
input: { camera },
|
|
420
|
-
}).start();
|
|
421
|
-
|
|
422
|
-
actor.send({ type: 'START' });
|
|
423
|
-
|
|
424
|
-
await vi.waitFor(
|
|
425
|
-
() => {
|
|
426
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
427
|
-
throw new Error('Not in error state yet');
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
{ timeout: 5000, interval: 50 },
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
const errorSnapshot = actor.getSnapshot();
|
|
434
|
-
expect(errorSnapshot.value).toBe('error');
|
|
435
|
-
expect(errorSnapshot.context.errorType).toBe('not_readable');
|
|
436
|
-
|
|
437
|
-
actor.stop();
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it('should set error type for OverconstrainedError', async () => {
|
|
441
|
-
const camera = new MockCameraProvider();
|
|
442
|
-
camera.setShouldReject(
|
|
443
|
-
true,
|
|
444
|
-
new DOMException('Constraints not supported', 'OverconstrainedError'),
|
|
445
|
-
);
|
|
446
|
-
const actor = createActor(cameraMachine, {
|
|
447
|
-
input: { camera },
|
|
448
|
-
}).start();
|
|
449
|
-
|
|
450
|
-
actor.send({ type: 'START' });
|
|
451
|
-
|
|
452
|
-
await vi.waitFor(
|
|
453
|
-
() => {
|
|
454
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
455
|
-
throw new Error('Not in error state yet');
|
|
456
|
-
}
|
|
457
|
-
},
|
|
458
|
-
{ timeout: 5000, interval: 50 },
|
|
459
|
-
);
|
|
460
|
-
|
|
461
|
-
const errorSnapshot = actor.getSnapshot();
|
|
462
|
-
expect(errorSnapshot.value).toBe('error');
|
|
463
|
-
expect(errorSnapshot.context.errorType).toBe('overconstrained');
|
|
464
|
-
|
|
465
|
-
actor.stop();
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('should set error type for SecurityError', async () => {
|
|
469
|
-
const camera = new MockCameraProvider();
|
|
470
|
-
camera.setShouldReject(
|
|
471
|
-
true,
|
|
472
|
-
new DOMException('Security error', 'SecurityError'),
|
|
473
|
-
);
|
|
474
|
-
const actor = createActor(cameraMachine, {
|
|
475
|
-
input: { camera },
|
|
476
|
-
}).start();
|
|
477
|
-
|
|
478
|
-
actor.send({ type: 'START' });
|
|
479
|
-
|
|
480
|
-
await vi.waitFor(
|
|
481
|
-
() => {
|
|
482
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
483
|
-
throw new Error('Not in error state yet');
|
|
484
|
-
}
|
|
485
|
-
},
|
|
486
|
-
{ timeout: 5000, interval: 50 },
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
const errorSnapshot = actor.getSnapshot();
|
|
490
|
-
expect(errorSnapshot.value).toBe('error');
|
|
491
|
-
expect(errorSnapshot.context.errorType).toBe('security');
|
|
492
|
-
|
|
493
|
-
actor.stop();
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
it('should set error type for AbortError', async () => {
|
|
497
|
-
const camera = new MockCameraProvider();
|
|
498
|
-
camera.setShouldReject(
|
|
499
|
-
true,
|
|
500
|
-
new DOMException('Request aborted', 'AbortError'),
|
|
501
|
-
);
|
|
502
|
-
const actor = createActor(cameraMachine, {
|
|
503
|
-
input: { camera },
|
|
504
|
-
}).start();
|
|
505
|
-
|
|
506
|
-
actor.send({ type: 'START' });
|
|
507
|
-
|
|
508
|
-
await vi.waitFor(
|
|
509
|
-
() => {
|
|
510
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
511
|
-
throw new Error('Not in error state yet');
|
|
512
|
-
}
|
|
513
|
-
},
|
|
514
|
-
{ timeout: 5000, interval: 50 },
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
const errorSnapshot = actor.getSnapshot();
|
|
518
|
-
expect(errorSnapshot.value).toBe('error');
|
|
519
|
-
expect(errorSnapshot.context.errorType).toBe('abort');
|
|
520
|
-
|
|
521
|
-
actor.stop();
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
it('should set error type to unknown for unrecognized errors', async () => {
|
|
525
|
-
const camera = new MockCameraProvider();
|
|
526
|
-
camera.setShouldReject(true, new Error('Unknown error'));
|
|
527
|
-
const actor = createActor(cameraMachine, {
|
|
528
|
-
input: { camera },
|
|
529
|
-
}).start();
|
|
530
|
-
|
|
531
|
-
actor.send({ type: 'START' });
|
|
532
|
-
|
|
533
|
-
await vi.waitFor(
|
|
534
|
-
() => {
|
|
535
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
536
|
-
throw new Error('Not in error state yet');
|
|
537
|
-
}
|
|
538
|
-
},
|
|
539
|
-
{ timeout: 5000, interval: 50 },
|
|
540
|
-
);
|
|
541
|
-
|
|
542
|
-
const errorSnapshot = actor.getSnapshot();
|
|
543
|
-
expect(errorSnapshot.value).toBe('error');
|
|
544
|
-
expect(errorSnapshot.context.errorType).toBe('unknown');
|
|
545
|
-
expect(errorSnapshot.context.error).toBe('Unknown error');
|
|
546
|
-
|
|
547
|
-
actor.stop();
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it('should handle error without message', async () => {
|
|
551
|
-
const camera = new MockCameraProvider();
|
|
552
|
-
const errorWithoutMessage = new Error();
|
|
553
|
-
errorWithoutMessage.message = '';
|
|
554
|
-
camera.setShouldReject(true, errorWithoutMessage);
|
|
555
|
-
const actor = createActor(cameraMachine, {
|
|
556
|
-
input: { camera },
|
|
557
|
-
}).start();
|
|
558
|
-
|
|
559
|
-
actor.send({ type: 'START' });
|
|
560
|
-
|
|
561
|
-
await vi.waitFor(
|
|
562
|
-
() => {
|
|
563
|
-
if (actor.getSnapshot().value !== 'error') {
|
|
564
|
-
throw new Error('Not in error state yet');
|
|
565
|
-
}
|
|
566
|
-
},
|
|
567
|
-
{ timeout: 5000, interval: 50 },
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
const errorSnapshot = actor.getSnapshot();
|
|
571
|
-
expect(errorSnapshot.value).toBe('error');
|
|
572
|
-
expect(errorSnapshot.context.error).toBeDefined();
|
|
573
|
-
expect(errorSnapshot.context.errorType).toBe('unknown');
|
|
574
|
-
|
|
575
|
-
actor.stop();
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
describe('Permission check error handling', () => {
|
|
580
|
-
it('should handle permission check failure gracefully', async () => {
|
|
581
|
-
const actor = createTestActor();
|
|
582
|
-
|
|
583
|
-
actor.send({ type: 'START' });
|
|
584
|
-
|
|
585
|
-
// Wait for permission check to complete (even if it fails)
|
|
586
|
-
await vi.waitFor(
|
|
587
|
-
() => {
|
|
588
|
-
const snapshot = actor.getSnapshot();
|
|
589
|
-
// Should transition to routing even on error (with prompt as default)
|
|
590
|
-
return (
|
|
591
|
-
snapshot.value === 'routing' ||
|
|
592
|
-
snapshot.value === 'requesting' ||
|
|
593
|
-
snapshot.value === 'streaming'
|
|
594
|
-
);
|
|
595
|
-
},
|
|
596
|
-
{ timeout: 2000 },
|
|
597
|
-
);
|
|
598
|
-
|
|
599
|
-
actor.stop();
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
});
|