audiocontext-sinkid-setsinkid.https.html (4923B)
1 <!DOCTYPE html> 2 <head> 3 <title>Test AudioContext.setSinkId() method</title> 4 </head> 5 <script src=/resources/testharness.js></script> 6 <script src=/resources/testharnessreport.js></script> 7 <script> 8 "use strict"; 9 10 const audioContext = new AudioContext(); 11 let outputDeviceList = null; 12 let firstDeviceId = null; 13 14 // Setup: Get permission via getUserMedia() and a list of audio output devices. 15 promise_setup(async t => { 16 await navigator.mediaDevices.getUserMedia({ audio: true }); 17 const deviceList = await navigator.mediaDevices.enumerateDevices(); 18 outputDeviceList = 19 deviceList.filter(({kind}) => kind === 'audiooutput'); 20 assert_greater_than(outputDeviceList.length, 1, 21 'the system must have more than 1 device.'); 22 firstDeviceId = outputDeviceList[1].deviceId; 23 }, 'Get permission via getUserMedia() and a list of audio output devices.'); 24 25 26 // 1.2.3. AudioContext.setSinkId() method 27 // https://webaudio.github.io/web-audio-api/#dom-audiocontext-setsinkid-domstring-or-audiosinkoptions-sinkid 28 29 promise_test(async t => { 30 t.step(() => { 31 // The default value of `sinkId` is the empty string. 32 assert_equals(audioContext.sinkId, ''); 33 }); 34 t.done(); 35 }, 'setSinkId() with a valid device identifier should succeeded.'); 36 37 promise_test(async t => { 38 // Change to the first non-default device in the list. 39 await audioContext.setSinkId(firstDeviceId); 40 t.step(() => { 41 // If both `sinkId` and [[sink ID]] are a type of DOMString, and they are 42 // equal to each other, resolve the promise immediately. 43 assert_equals(typeof audioContext.sinkId, 'string'); 44 assert_equals(audioContext.sinkId, firstDeviceId); 45 }); 46 return audioContext.setSinkId(firstDeviceId); 47 }, 'setSinkId() with the same sink ID should resolve immediately.'); 48 49 promise_test(async t => { 50 // If sinkId is a type of AudioSinkOptions and [[sink ID]] is a type of 51 // AudioSinkInfo, and type in sinkId and type in [[sink ID]] are equal, 52 // resolve the promise immediately. 53 await audioContext.setSinkId({type: 'none'}); 54 t.step(() => { 55 assert_equals(typeof audioContext.sinkId, 'object'); 56 assert_equals(audioContext.sinkId.type, 'none'); 57 }); 58 return audioContext.setSinkId({type: 'none'}); 59 }, 'setSinkId() with the same AudioSinkOptions.type value should resolve ' + 60 'immediately.'); 61 62 // 1.2.4. Validating sinkId 63 // https://webaudio.github.io/web-audio-api/#validating-sink-identifier 64 65 // Step 3. If document is not allowed to use the feature identified by 66 // "speaker-selection", return a new DOMException whose name is 67 // "NotAllowedError". 68 // TODO: Due to the lack of implementation, this step is not tested. 69 70 // The wrong AudioSinkOption.type should cause a TypeError. 71 promise_test(t => 72 promise_rejects_js(t, TypeError, 73 audioContext.setSinkId({type: 'something_else'})), 74 'setSinkId() should fail with TypeError on an invalid ' + 75 'AudioSinkOptions.type value.'); 76 77 // Step 4. If sinkId is a type of DOMString but it is not equal to the empty 78 // string or it does not match any audio output device identified by the result 79 // that would be provided by enumerateDevices(), return a new DOMException whose 80 // name is "NotFoundError". 81 promise_test(t => 82 promise_rejects_dom(t, 'NotFoundError', 83 audioContext.setSinkId('some_random_device_id')), 84 'setSinkId() should fail with NotFoundError on an invalid device ' + 85 'identifier.'); 86 87 // setSinkId invoked from closed AudioContext should throw InvalidStateError 88 // DOMException. 89 promise_test(async t => { 90 await audioContext.close(); 91 t.step(() => { 92 assert_equals(audioContext.state, 'closed'); 93 }); 94 promise_rejects_dom(t, 'InvalidStateError', 95 audioContext.setSinkId('some_random_device_id')) 96 },'setSinkId() should fail with InvalidStateError when calling from a' + 97 'stopped AudioContext'); 98 99 // setSinkId invoked from detached document should throw InvalidStateError 100 // DOMException. 101 promise_test(async t => { 102 const iframe = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe"); 103 document.body.appendChild(iframe); 104 let iframeAudioContext = new iframe.contentWindow.AudioContext(); 105 document.body.removeChild(iframe); 106 promise_rejects_dom(t, 'InvalidStateError', 107 iframeAudioContext.setSinkId('some_random_device_id')); 108 },'setSinkId() should fail with InvalidStateError when calling from a' + 109 'detached document'); 110 111 // Pending setSinkId() promises should be rejected with a 112 // DOMException of InvalidStateError when the context is closed. 113 // See: crbug.com/1408376 114 promise_test(async t => { 115 const audioContext = new AudioContext(); 116 promise_rejects_dom(t, 'InvalidStateError', 117 audioContext.setSinkId('some_random_device_id')); 118 await audioContext.close(); 119 },'pending setSinkId() should be rejected with InvalidStateError when' + 120 'AudioContext is closed'); 121 </script> 122 </html>