1
+ <!DOCTYPE html>
2
+ < html >
3
+ < head >
4
+ < meta charset ="utf-8 ">
5
+ < meta name ="viewport " content ="width=device-width ">
6
+ < title > enumerateDevices API Test</ title >
7
+ < link rel ="stylesheet " href ="../../shared/style.css ">
8
+ </ head >
9
+ < body >
10
+ < script src ="../../shared/utils.js "> </ script >
11
+ < p > < a href ="../index.html "> [Webcompat API Tests]</ a > </ p >
12
+
13
+ < p > This page tests the enumerateDevices API proxy functionality</ p >
14
+
15
+ < script >
16
+ // Mock device data for testing
17
+ const mockDevices = [
18
+ {
19
+ deviceId : 'audioinput-1' ,
20
+ kind : 'audioinput' ,
21
+ label : 'Test Microphone' ,
22
+ groupId : 'group-1'
23
+ } ,
24
+ {
25
+ deviceId : 'audiooutput-1' ,
26
+ kind : 'audiooutput' ,
27
+ label : 'Test Speaker' ,
28
+ groupId : 'group-1'
29
+ } ,
30
+ {
31
+ deviceId : 'videoinput-1' ,
32
+ kind : 'videoinput' ,
33
+ label : 'Test Camera' ,
34
+ groupId : 'group-2'
35
+ }
36
+ ] ;
37
+
38
+ // Helper function to create device objects with proper prototype
39
+ function createDeviceObject ( deviceData ) {
40
+ const device = Object . create (
41
+ deviceData . kind === 'videoinput' || deviceData . kind === 'audioinput'
42
+ ? InputDeviceInfo . prototype
43
+ : MediaDeviceInfo . prototype
44
+ ) ;
45
+
46
+ Object . defineProperties ( device , {
47
+ deviceId : { value : deviceData . deviceId , writable : false , enumerable : true } ,
48
+ kind : { value : deviceData . kind , writable : false , enumerable : true } ,
49
+ label : { value : deviceData . label , writable : false , enumerable : true } ,
50
+ groupId : { value : deviceData . groupId , writable : false , enumerable : true }
51
+ } ) ;
52
+
53
+ // Add toJSON method to avoid "Illegal invocation" errors
54
+ device . toJSON = function ( ) {
55
+ return {
56
+ deviceId : this . deviceId ,
57
+ kind : this . kind ,
58
+ label : this . label ,
59
+ groupId : this . groupId
60
+ } ;
61
+ } ;
62
+
63
+ return device ;
64
+ }
65
+
66
+ // Mock navigator.mediaDevices.enumerateDevices
67
+ const originalEnumerateDevices = navigator . mediaDevices . enumerateDevices ;
68
+ navigator . mediaDevices . enumerateDevices = async function ( ) {
69
+ return mockDevices . map ( createDeviceObject ) ;
70
+ } ;
71
+
72
+ test ( 'Native enumerateDevices behavior' , async ( ) => {
73
+ const results = [ ] ;
74
+
75
+ try {
76
+ const devices = await navigator . mediaDevices . enumerateDevices ( ) ;
77
+
78
+ results . push ( {
79
+ name : 'Returns array of devices' ,
80
+ result : Array . isArray ( devices ) ,
81
+ expected : true
82
+ } ) ;
83
+
84
+ results . push ( {
85
+ name : 'Returns correct number of devices' ,
86
+ result : devices . length ,
87
+ expected : 3
88
+ } ) ;
89
+
90
+ results . push ( {
91
+ name : 'Devices have correct structure' ,
92
+ result : devices . every ( device =>
93
+ device . deviceId &&
94
+ device . kind &&
95
+ device . label &&
96
+ device . groupId
97
+ ) ,
98
+ expected : true
99
+ } ) ;
100
+
101
+ results . push ( {
102
+ name : 'Devices are MediaDeviceInfo instances' ,
103
+ result : devices . every ( device => device instanceof MediaDeviceInfo ) ,
104
+ expected : true
105
+ } ) ;
106
+
107
+ results . push ( {
108
+ name : 'Input devices are InputDeviceInfo instances' ,
109
+ result : devices . filter ( d => d . kind === 'audioinput' || d . kind === 'videoinput' )
110
+ . every ( device => device instanceof InputDeviceInfo ) ,
111
+ expected : true
112
+ } ) ;
113
+
114
+ results . push ( {
115
+ name : 'toJSON method works without errors' ,
116
+ result : ( ( ) => {
117
+ try {
118
+ devices . forEach ( device => device . toJSON ( ) ) ;
119
+ return true ;
120
+ } catch ( e ) {
121
+ return false ;
122
+ }
123
+ } ) ( ) ,
124
+ expected : true
125
+ } ) ;
126
+
127
+ results . push ( {
128
+ name : 'Device properties are read-only' ,
129
+ result : ( ( ) => {
130
+ try {
131
+ devices [ 0 ] . deviceId = 'modified' ;
132
+ return false ;
133
+ } catch ( e ) {
134
+ return true ;
135
+ }
136
+ } ) ( ) ,
137
+ expected : true
138
+ } ) ;
139
+
140
+ results . push ( {
141
+ name : 'Device IDs are unique' ,
142
+ result : new Set ( devices . map ( d => d . deviceId ) ) . size === devices . length ,
143
+ expected : true
144
+ } ) ;
145
+
146
+ results . push ( {
147
+ name : 'Group IDs are valid' ,
148
+ result : devices . every ( device => typeof device . groupId === 'string' && device . groupId . length > 0 ) ,
149
+ expected : true
150
+ } ) ;
151
+
152
+ } catch ( error ) {
153
+ results . push ( {
154
+ name : 'No errors thrown' ,
155
+ result : false ,
156
+ expected : true
157
+ } ) ;
158
+ }
159
+
160
+ return results ;
161
+ } ) ;
162
+
163
+ test ( 'Feature-enabled enumerateDevices behavior' , async ( ) => {
164
+ const results = [ ] ;
165
+
166
+ // This test would run when the feature is enabled
167
+ // The actual behavior should be the same as native since we're just proxying
168
+ try {
169
+ const devices = await navigator . mediaDevices . enumerateDevices ( ) ;
170
+
171
+ results . push ( {
172
+ name : 'Feature-enabled returns same number of devices' ,
173
+ result : devices . length ,
174
+ expected : 3
175
+ } ) ;
176
+
177
+ results . push ( {
178
+ name : 'Feature-enabled devices maintain structure' ,
179
+ result : devices . every ( device =>
180
+ device . deviceId &&
181
+ device . kind &&
182
+ device . label &&
183
+ device . groupId
184
+ ) ,
185
+ expected : true
186
+ } ) ;
187
+
188
+ results . push ( {
189
+ name : 'Feature-enabled devices are proper instances' ,
190
+ result : devices . every ( device => device instanceof MediaDeviceInfo ) ,
191
+ expected : true
192
+ } ) ;
193
+
194
+ } catch ( error ) {
195
+ results . push ( {
196
+ name : 'Feature-enabled no errors thrown' ,
197
+ result : false ,
198
+ expected : true
199
+ } ) ;
200
+ }
201
+
202
+ return results ;
203
+ } ) ;
204
+
205
+ test ( 'Error handling' , async ( ) => {
206
+ const results = [ ] ;
207
+
208
+ // Test with a broken enumerateDevices
209
+ const originalEnumerateDevices = navigator . mediaDevices . enumerateDevices ;
210
+ navigator . mediaDevices . enumerateDevices = async function ( ) {
211
+ throw new Error ( 'Permission denied' ) ;
212
+ } ;
213
+
214
+ try {
215
+ await navigator . mediaDevices . enumerateDevices ( ) ;
216
+ results . push ( {
217
+ name : 'Error should be thrown' ,
218
+ result : false ,
219
+ expected : true
220
+ } ) ;
221
+ } catch ( error ) {
222
+ results . push ( {
223
+ name : 'Error is properly thrown' ,
224
+ result : error . message === 'Permission denied' ,
225
+ expected : true
226
+ } ) ;
227
+ }
228
+
229
+ // Restore original
230
+ navigator . mediaDevices . enumerateDevices = originalEnumerateDevices ;
231
+
232
+ return results ;
233
+ } ) ;
234
+
235
+ // eslint-disable-next-line no-undef
236
+ renderResults ( ) ;
237
+ </ script >
238
+ </ body >
239
+ </ html >
0 commit comments