audiocontext-sinkid-constructor.https.html (4969B)
1 <!DOCTYPE html> 2 <head> 3 <title>Test AudioContext constructor with sinkId options</title> 4 </head> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script> 8 "use strict"; 9 10 let outputDeviceList = null; 11 let firstDeviceId = null; 12 13 navigator.mediaDevices.getUserMedia({audio: true}).then(() => { 14 navigator.mediaDevices.enumerateDevices().then((deviceList) => { 15 outputDeviceList = 16 deviceList.filter(({kind}) => kind === 'audiooutput'); 17 assert_greater_than(outputDeviceList.length, 1, 18 'the system must have more than 1 device.'); 19 firstDeviceId = outputDeviceList[1].deviceId; 20 21 // Run async tests concurrently. 22 async_test(t => testDefaultSinkId(t), 23 'Setting sinkId to the empty string at construction should ' + 24 'succeed.'); 25 async_test(t => testValidSinkId(t), 26 'Setting sinkId with a valid device identifier at ' + 27 'construction should succeed.'); 28 async_test(t => testAudioSinkOptions(t), 29 'Setting sinkId with an AudioSinkOptions at construction ' + 30 'should succeed.'); 31 async_test(t => testOnErrorWrongType(t), 32 'Invalid sinkId argument with a wrong type should throw '+ 33 'an appropriate exception.'); 34 async_test(t => testOnErrorWrongId(t), 35 'Invalid sinkId argument with a wrong ID should dispatch ' + 36 'an onerror event.') 37 }); 38 }); 39 40 // 1.2.1. AudioContext constructor 41 // https://webaudio.github.io/web-audio-api/#AudioContext-constructors 42 43 // Step 10.1.1. If sinkId is equal to [[sink ID]], abort these substeps. 44 const testDefaultSinkId = (t) => { 45 // The initial `sinkId` is the empty string. This will cause the same value 46 // check. 47 const audioContext = new AudioContext({sinkId: ''}); 48 audioContext.addEventListener('statechange', () => { 49 t.step(() => { 50 assert_equals(audioContext.sinkId, ''); 51 assert_equals(audioContext.state, 'running'); 52 }); 53 audioContext.close(); 54 t.done(); 55 }, {once: true}); 56 }; 57 58 // Step 10.1.2~3: See "Validating sinkId" tests below. 59 60 // Step 10.1.4. If sinkId is a type of DOMString, set [[sink ID]] to sinkId and 61 // abort these substeps. 62 const testValidSinkId = (t) => { 63 const audioContext = new AudioContext({sinkId: firstDeviceId}); 64 audioContext.addEventListener('statechange', () => { 65 t.step(() => { 66 assert_true(audioContext.sinkId === firstDeviceId, 67 'the context sinkId should match the given sinkId.'); 68 }); 69 audioContext.close(); 70 t.done(); 71 }, {once: true}); 72 t.step_timeout(t.unreached_func('onstatechange not fired or assert failed'), 73 100); 74 }; 75 76 // Step 10.1.5. If sinkId is a type of AudioSinkOptions, set [[sink ID]] to a 77 // new instance of AudioSinkInfo created with the value of type of sinkId. 78 const testAudioSinkOptions = (t) => { 79 const audioContext = new AudioContext({sinkId: {type: 'none'}}); 80 // The only signal we can use for the sinkId change after construction is 81 // `statechange` event. 82 audioContext.addEventListener('statechange', () => { 83 t.step(() => { 84 assert_equals(typeof audioContext.sinkId, 'object'); 85 assert_equals(audioContext.sinkId.type, 'none'); 86 }); 87 audioContext.close(); 88 t.done(); 89 }, {once: true}); 90 t.step_timeout(t.unreached_func('onstatechange not fired or assert failed'), 91 100); 92 }; 93 94 // 1.2.4. Validating sinkId 95 // https://webaudio.github.io/web-audio-api/#validating-sink-identifier 96 97 // Step 3. If document is not allowed to use the feature identified by 98 // "speaker-selection", return false. 99 // TODO(https://crbug.com/1380872): Due to the lack of "speaker-selection" 100 // implementation, a test for such step does not exist yet. 101 102 const testOnErrorWrongType = (t) => { 103 t.step(() => { 104 // The wrong AudioSinkOption.type should cause a TypeError. This exception 105 // is thrown at the IDL validation level. 106 assert_throws_js(TypeError, () => { 107 const audioContext = new AudioContext({sinkId: {type: 'something_else'}}); 108 audioContext.close(); 109 }, 'An invalid AudioSinkOptions.type value should throw a TypeError ' + 110 'exception.'); 111 }); 112 t.done(); 113 }; 114 115 // Step 4. If sinkIdArg is a type of DOMString but it is not equal to the 116 // empty string or it does not match any audio output device identified by 117 // the result that would be provided by enumerateDevices(), return false. 118 // 119 // When validation returns a false value, the context dispatches an onerror 120 // event. 121 const testOnErrorWrongId = (t) => { 122 // An invalid device ID should dispatch an onerror event. 123 const audioContext = new AudioContext({sinkId: 'some_random_device_id'}); 124 audioContext.addEventListener('error', () => { 125 audioContext.close(); 126 t.done(); 127 }, {once: true}); 128 t.step_timeout(t.unreached_func('onerror not fired on invalid device ID'), 129 100); 130 }; 131 </script> 132 </html>