testLegacyEnumerateDevices.js (5474B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 /* 5 This is a modified copy of test_enumerateDevices.html testing the 6 enumerateDevices() legacy version and deviceId constraint. 7 */ 8 9 async function mustSucceedWithStream(msg, f) { 10 try { 11 const stream = await f(); 12 for (const track of stream.getTracks()) { 13 track.stop(); 14 } 15 ok(true, msg + " must succeed"); 16 } catch (e) { 17 is(e.name, null, msg + " must succeed: " + e.message); 18 } 19 } 20 21 async function mustFailWith(msg, reason, constraint, f) { 22 try { 23 await f(); 24 ok(false, msg + " must fail"); 25 } catch (e) { 26 is(e.name, reason, msg + " must fail: " + e.message); 27 if (constraint) { 28 is(e.constraint, constraint, msg + " must fail w/correct constraint."); 29 } 30 } 31 } 32 33 const gUM = c => navigator.mediaDevices.getUserMedia(c); 34 35 const kinds = ["videoinput", "audioinput", "audiooutput"]; 36 37 function validateDevice({ kind, label, deviceId, groupId }) { 38 ok(kinds.includes(kind), "Known device kind"); 39 is(deviceId.length, 44, "deviceId length id as expected for Firefox"); 40 ok(label.length !== undefined, "Device label: " + label); 41 isnot(groupId, "", "groupId must be present."); 42 } 43 44 // Validate enumerated devices before gUM (legacy). 45 46 async function testLegacyEnumerateDevices() { 47 let devices = await navigator.mediaDevices.enumerateDevices(); 48 ok(devices.length, "At least one device found"); 49 const jsoned = JSON.parse(JSON.stringify(devices)); 50 is(jsoned[0].kind, devices[0].kind, "kind survived serializer"); 51 is(jsoned[0].deviceId, devices[0].deviceId, "deviceId survived serializer"); 52 for (const device of devices) { 53 validateDevice(device); 54 if (device.kind == "audiooutput") { 55 continue; 56 } 57 is(device.label, "", "Device label is empty"); 58 // Test deviceId constraint 59 let deviceId = device.deviceId; 60 let constraints = 61 device.kind == "videoinput" 62 ? { video: { deviceId } } 63 : { audio: { deviceId } }; 64 let namedDevices; 65 for (const track of (await gUM(constraints)).getTracks()) { 66 is(typeof track.label, "string", "Track label is a string"); 67 isnot(track.label.length, 0, "Track label is not empty"); 68 if (!namedDevices) { 69 namedDevices = await navigator.mediaDevices.enumerateDevices(); 70 } 71 const namedDevice = namedDevices.find(d => d.deviceId == device.deviceId); 72 is(track.label, namedDevice.label, "Track label is the device label"); 73 track.stop(); 74 } 75 } 76 77 const unknownId = "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc="; 78 79 // Check deviceId failure paths for video. 80 81 await mustSucceedWithStream("unknown plain deviceId on video", () => 82 gUM({ video: { deviceId: unknownId } }) 83 ); 84 await mustSucceedWithStream("unknown plain deviceId on audio", () => 85 gUM({ audio: { deviceId: unknownId } }) 86 ); 87 await mustFailWith( 88 "unknown exact deviceId on video", 89 "OverconstrainedError", 90 "deviceId", 91 () => gUM({ video: { deviceId: { exact: unknownId } } }) 92 ); 93 await mustFailWith( 94 "unknown exact deviceId on audio", 95 "OverconstrainedError", 96 "deviceId", 97 () => gUM({ audio: { deviceId: { exact: unknownId } } }) 98 ); 99 100 // Check that deviceIds are stable for same origin and differ across origins. 101 102 const path = 103 "/tests/dom/media/webrtc/tests/mochitests/test_enumerateDevices_iframe_pre_gum.html"; 104 const origins = ["https://example.com", "https://test1.example.com"]; 105 info(window.location); 106 107 const haveDevicesMap = new Promise(resolve => { 108 const map = new Map(); 109 window.addEventListener("message", ({ origin, data }) => { 110 ok(origins.includes(origin), "Got message from expected origin"); 111 map.set(origin, JSON.parse(data)); 112 if (map.size < origins.length) { 113 return; 114 } 115 resolve(map); 116 }); 117 }); 118 119 await Promise.all( 120 origins.map(origin => { 121 const iframe = document.createElement("iframe"); 122 iframe.src = origin + path; 123 iframe.allow = "camera;microphone;speaker-selection"; 124 info(iframe.src); 125 document.documentElement.appendChild(iframe); 126 return new Promise(resolve => (iframe.onload = resolve)); 127 }) 128 ); 129 let devicesMap = await haveDevicesMap; 130 let [sameOriginDevices, differentOriginDevices] = origins.map(o => 131 devicesMap.get(o) 132 ); 133 134 is(sameOriginDevices.length, devices.length, "same origin same devices"); 135 is( 136 differentOriginDevices.length, 137 devices.length, 138 "cross origin same devices" 139 ); 140 [...sameOriginDevices, ...differentOriginDevices].forEach(d => 141 validateDevice(d) 142 ); 143 144 for (const device of sameOriginDevices) { 145 ok( 146 devices.find(d => d.deviceId == device.deviceId), 147 "Same origin deviceId for " + device.label + " must match" 148 ); 149 } 150 for (const device of differentOriginDevices) { 151 ok( 152 !devices.find(d => d.deviceId == device.deviceId), 153 "Different origin deviceId for " + device.label + " must be different" 154 ); 155 } 156 157 // Check the special case of no devices found. 158 await pushPrefs( 159 ["media.navigator.streams.fake", false], 160 ["media.audio_loopback_dev", "none"], 161 ["media.video_loopback_dev", "none"] 162 ); 163 devices = await navigator.mediaDevices.enumerateDevices(); 164 devices = devices.filter(({ kind }) => kind != "audiooutput"); 165 is(devices.length, 0, "No devices"); 166 }