Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 477a13d

Browse files
authored
[camera_web] Add camera errors handling (#4207)
1 parent 97fa266 commit 477a13d

15 files changed

+1596
-711
lines changed

packages/camera/camera_web/example/integration_test/camera_settings_test.dart

Lines changed: 314 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:ui';
88
import 'package:camera_platform_interface/camera_platform_interface.dart';
99
import 'package:camera_web/src/camera_settings.dart';
1010
import 'package:camera_web/src/types/types.dart';
11+
import 'package:flutter/services.dart';
1112
import 'package:flutter_test/flutter_test.dart';
1213
import 'package:integration_test/integration_test.dart';
1314
import 'package:mocktail/mocktail.dart';
@@ -18,6 +19,8 @@ void main() {
1819
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1920

2021
group('CameraSettings', () {
22+
const cameraId = 0;
23+
2124
late Window window;
2225
late Navigator navigator;
2326
late MediaDevices mediaDevices;
@@ -34,20 +37,325 @@ void main() {
3437
settings = CameraSettings()..window = window;
3538
});
3639

40+
group('getMediaStreamForOptions', () {
41+
testWidgets(
42+
'calls MediaDevices.getUserMedia '
43+
'with provided options', (tester) async {
44+
when(() => mediaDevices.getUserMedia(any()))
45+
.thenAnswer((_) async => FakeMediaStream([]));
46+
47+
final options = CameraOptions(
48+
video: VideoConstraints(
49+
facingMode: FacingModeConstraint.exact(CameraType.user),
50+
width: VideoSizeConstraint(ideal: 200),
51+
),
52+
);
53+
54+
await settings.getMediaStreamForOptions(options);
55+
56+
verify(
57+
() => mediaDevices.getUserMedia(options.toJson()),
58+
).called(1);
59+
});
60+
61+
testWidgets(
62+
'throws PlatformException '
63+
'with notSupported error '
64+
'when there are no media devices', (tester) async {
65+
when(() => navigator.mediaDevices).thenReturn(null);
66+
67+
expect(
68+
() => settings.getMediaStreamForOptions(CameraOptions()),
69+
throwsA(
70+
isA<PlatformException>().having(
71+
(e) => e.code,
72+
'code',
73+
CameraErrorCode.notSupported.toString(),
74+
),
75+
),
76+
);
77+
});
78+
79+
group('throws CameraWebException', () {
80+
testWidgets(
81+
'with notFound error '
82+
'when MediaDevices.getUserMedia throws DomException '
83+
'with NotFoundError', (tester) async {
84+
when(() => mediaDevices.getUserMedia(any()))
85+
.thenThrow(FakeDomException('NotFoundError'));
86+
87+
expect(
88+
() => settings.getMediaStreamForOptions(
89+
CameraOptions(),
90+
cameraId: cameraId,
91+
),
92+
throwsA(
93+
isA<CameraWebException>()
94+
.having((e) => e.cameraId, 'cameraId', cameraId)
95+
.having((e) => e.code, 'code', CameraErrorCode.notFound),
96+
),
97+
);
98+
});
99+
100+
testWidgets(
101+
'with notFound error '
102+
'when MediaDevices.getUserMedia throws DomException '
103+
'with DevicesNotFoundError', (tester) async {
104+
when(() => mediaDevices.getUserMedia(any()))
105+
.thenThrow(FakeDomException('DevicesNotFoundError'));
106+
107+
expect(
108+
() => settings.getMediaStreamForOptions(
109+
CameraOptions(),
110+
cameraId: cameraId,
111+
),
112+
throwsA(
113+
isA<CameraWebException>()
114+
.having((e) => e.cameraId, 'cameraId', cameraId)
115+
.having((e) => e.code, 'code', CameraErrorCode.notFound),
116+
),
117+
);
118+
});
119+
120+
testWidgets(
121+
'with notReadable error '
122+
'when MediaDevices.getUserMedia throws DomException '
123+
'with NotReadableError', (tester) async {
124+
when(() => mediaDevices.getUserMedia(any()))
125+
.thenThrow(FakeDomException('NotReadableError'));
126+
127+
expect(
128+
() => settings.getMediaStreamForOptions(
129+
CameraOptions(),
130+
cameraId: cameraId,
131+
),
132+
throwsA(
133+
isA<CameraWebException>()
134+
.having((e) => e.cameraId, 'cameraId', cameraId)
135+
.having((e) => e.code, 'code', CameraErrorCode.notReadable),
136+
),
137+
);
138+
});
139+
140+
testWidgets(
141+
'with notReadable error '
142+
'when MediaDevices.getUserMedia throws DomException '
143+
'with TrackStartError', (tester) async {
144+
when(() => mediaDevices.getUserMedia(any()))
145+
.thenThrow(FakeDomException('TrackStartError'));
146+
147+
expect(
148+
() => settings.getMediaStreamForOptions(
149+
CameraOptions(),
150+
cameraId: cameraId,
151+
),
152+
throwsA(
153+
isA<CameraWebException>()
154+
.having((e) => e.cameraId, 'cameraId', cameraId)
155+
.having((e) => e.code, 'code', CameraErrorCode.notReadable),
156+
),
157+
);
158+
});
159+
160+
testWidgets(
161+
'with overconstrained error '
162+
'when MediaDevices.getUserMedia throws DomException '
163+
'with OverconstrainedError', (tester) async {
164+
when(() => mediaDevices.getUserMedia(any()))
165+
.thenThrow(FakeDomException('OverconstrainedError'));
166+
167+
expect(
168+
() => settings.getMediaStreamForOptions(
169+
CameraOptions(),
170+
cameraId: cameraId,
171+
),
172+
throwsA(
173+
isA<CameraWebException>()
174+
.having((e) => e.cameraId, 'cameraId', cameraId)
175+
.having(
176+
(e) => e.code, 'code', CameraErrorCode.overconstrained),
177+
),
178+
);
179+
});
180+
181+
testWidgets(
182+
'with overconstrained error '
183+
'when MediaDevices.getUserMedia throws DomException '
184+
'with ConstraintNotSatisfiedError', (tester) async {
185+
when(() => mediaDevices.getUserMedia(any()))
186+
.thenThrow(FakeDomException('ConstraintNotSatisfiedError'));
187+
188+
expect(
189+
() => settings.getMediaStreamForOptions(
190+
CameraOptions(),
191+
cameraId: cameraId,
192+
),
193+
throwsA(
194+
isA<CameraWebException>()
195+
.having((e) => e.cameraId, 'cameraId', cameraId)
196+
.having(
197+
(e) => e.code, 'code', CameraErrorCode.overconstrained),
198+
),
199+
);
200+
});
201+
202+
testWidgets(
203+
'with permissionDenied error '
204+
'when MediaDevices.getUserMedia throws DomException '
205+
'with NotAllowedError', (tester) async {
206+
when(() => mediaDevices.getUserMedia(any()))
207+
.thenThrow(FakeDomException('NotAllowedError'));
208+
209+
expect(
210+
() => settings.getMediaStreamForOptions(
211+
CameraOptions(),
212+
cameraId: cameraId,
213+
),
214+
throwsA(
215+
isA<CameraWebException>()
216+
.having((e) => e.cameraId, 'cameraId', cameraId)
217+
.having(
218+
(e) => e.code, 'code', CameraErrorCode.permissionDenied),
219+
),
220+
);
221+
});
222+
223+
testWidgets(
224+
'with permissionDenied error '
225+
'when MediaDevices.getUserMedia throws DomException '
226+
'with PermissionDeniedError', (tester) async {
227+
when(() => mediaDevices.getUserMedia(any()))
228+
.thenThrow(FakeDomException('PermissionDeniedError'));
229+
230+
expect(
231+
() => settings.getMediaStreamForOptions(
232+
CameraOptions(),
233+
cameraId: cameraId,
234+
),
235+
throwsA(
236+
isA<CameraWebException>()
237+
.having((e) => e.cameraId, 'cameraId', cameraId)
238+
.having(
239+
(e) => e.code, 'code', CameraErrorCode.permissionDenied),
240+
),
241+
);
242+
});
243+
244+
testWidgets(
245+
'with type error '
246+
'when MediaDevices.getUserMedia throws DomException '
247+
'with TypeError', (tester) async {
248+
when(() => mediaDevices.getUserMedia(any()))
249+
.thenThrow(FakeDomException('TypeError'));
250+
251+
expect(
252+
() => settings.getMediaStreamForOptions(
253+
CameraOptions(),
254+
cameraId: cameraId,
255+
),
256+
throwsA(
257+
isA<CameraWebException>()
258+
.having((e) => e.cameraId, 'cameraId', cameraId)
259+
.having((e) => e.code, 'code', CameraErrorCode.type),
260+
),
261+
);
262+
});
263+
264+
testWidgets(
265+
'with abort error '
266+
'when MediaDevices.getUserMedia throws DomException '
267+
'with AbortError', (tester) async {
268+
when(() => mediaDevices.getUserMedia(any()))
269+
.thenThrow(FakeDomException('AbortError'));
270+
271+
expect(
272+
() => settings.getMediaStreamForOptions(
273+
CameraOptions(),
274+
cameraId: cameraId,
275+
),
276+
throwsA(
277+
isA<CameraWebException>()
278+
.having((e) => e.cameraId, 'cameraId', cameraId)
279+
.having((e) => e.code, 'code', CameraErrorCode.abort),
280+
),
281+
);
282+
});
283+
284+
testWidgets(
285+
'with security error '
286+
'when MediaDevices.getUserMedia throws DomException '
287+
'with SecurityError', (tester) async {
288+
when(() => mediaDevices.getUserMedia(any()))
289+
.thenThrow(FakeDomException('SecurityError'));
290+
291+
expect(
292+
() => settings.getMediaStreamForOptions(
293+
CameraOptions(),
294+
cameraId: cameraId,
295+
),
296+
throwsA(
297+
isA<CameraWebException>()
298+
.having((e) => e.cameraId, 'cameraId', cameraId)
299+
.having((e) => e.code, 'code', CameraErrorCode.security),
300+
),
301+
);
302+
});
303+
304+
testWidgets(
305+
'with unknown error '
306+
'when MediaDevices.getUserMedia throws DomException '
307+
'with an unknown error', (tester) async {
308+
when(() => mediaDevices.getUserMedia(any()))
309+
.thenThrow(FakeDomException('Unknown'));
310+
311+
expect(
312+
() => settings.getMediaStreamForOptions(
313+
CameraOptions(),
314+
cameraId: cameraId,
315+
),
316+
throwsA(
317+
isA<CameraWebException>()
318+
.having((e) => e.cameraId, 'cameraId', cameraId)
319+
.having((e) => e.code, 'code', CameraErrorCode.unknown),
320+
),
321+
);
322+
});
323+
324+
testWidgets(
325+
'with unknown error '
326+
'when MediaDevices.getUserMedia throws an unknown exception',
327+
(tester) async {
328+
when(() => mediaDevices.getUserMedia(any())).thenThrow(Exception());
329+
330+
expect(
331+
() => settings.getMediaStreamForOptions(
332+
CameraOptions(),
333+
cameraId: cameraId,
334+
),
335+
throwsA(
336+
isA<CameraWebException>()
337+
.having((e) => e.cameraId, 'cameraId', cameraId)
338+
.having((e) => e.code, 'code', CameraErrorCode.unknown),
339+
),
340+
);
341+
});
342+
});
343+
});
344+
37345
group('getFacingModeForVideoTrack', () {
38346
testWidgets(
39-
'throws CameraException '
347+
'throws PlatformException '
40348
'with notSupported error '
41349
'when there are no media devices', (tester) async {
42350
when(() => navigator.mediaDevices).thenReturn(null);
43351

44352
expect(
45353
() => settings.getFacingModeForVideoTrack(MockMediaStreamTrack()),
46354
throwsA(
47-
isA<CameraException>().having(
355+
isA<PlatformException>().having(
48356
(e) => e.code,
49357
'code',
50-
CameraErrorCodes.notSupported,
358+
CameraErrorCode.notSupported.toString(),
51359
),
52360
),
53361
);
@@ -145,7 +453,7 @@ void main() {
145453
});
146454

147455
testWidgets(
148-
'throws CameraException '
456+
'throws PlatformException '
149457
'with unknown error '
150458
'when getting the video track capabilities '
151459
'throws an unknown error', (tester) async {
@@ -157,10 +465,10 @@ void main() {
157465
expect(
158466
() => settings.getFacingModeForVideoTrack(videoTrack),
159467
throwsA(
160-
isA<CameraException>().having(
468+
isA<PlatformException>().having(
161469
(e) => e.code,
162470
'code',
163-
CameraErrorCodes.unknown,
471+
CameraErrorCode.unknown.toString(),
164472
),
165473
),
166474
);

0 commit comments

Comments
 (0)