| <!doctype html> | |
| <html> | |
| <head> | |
| <title>Test setSinkId behavior with permissions / device changes</title> | |
| <link rel="author" title="Dominique Hazael-Massieux" href="mailto:dom@w3.org"/> | |
| <link rel="help" href="https://www.w3.org/TR/audio-output/#dom-htmlmediaelement-setsinkid"> | |
| <meta name="timeout" content="long"> | |
| </head> | |
| <body> | |
| <h1 class="instructions">Description</h1> | |
| <p class="instructions">This test checks that <code>setSinkId</code> follows the algorithm, this includes manually checking the proper rendering on new output devices.</p> | |
| <p class="instructions">When prompted to access microphones, please accept as this is the only current way to get permissions for associated output devices.</p> | |
| <p class="instructions">For each authorized output device, check that selecting it makes the audio beat rendered on the corresponding device.</p> | |
| <p>Available but unauthorized devices (only those for which we can gain permission can be selected):</p> | |
| <ul id="available"></ul> | |
| <p>Authorized devices:</p> | |
| <ul id="authorized"></ul> | |
| <audio controls id="beat" src="/media/sound_5.mp3" loop></audio> | |
| <div id='log'></div> | |
| <script src=/resources/testharness.js></script> | |
| <script src=/resources/testharnessreport.js></script> | |
| <script> | |
| "use strict"; | |
| const is_output = d => d.kind === "audiooutput"; | |
| const by_id = (id) => d => d.groupId === id; | |
| const is_input = d => d.kind === "audioinput"; | |
| const audio = document.getElementById("beat"); | |
| const available = document.getElementById("available"); | |
| const authorized = document.getElementById("authorized"); | |
| let outputList; | |
| const selectDeviceTester = (t) => (e) => { | |
| const groupId = e.target.dataset["groupid"]; | |
| const device = outputList.find(by_id(groupId)); | |
| if (audio.paused) audio.play(); | |
| promise_test(pt => audio.setSinkId(device.deviceId).then(() => { | |
| assert_equals(device.deviceId, audio.sinkId); | |
| const pass = document.createElement("button"); | |
| const fail = document.createElement("button"); | |
| const result = (bool) => () => { | |
| assert_true(bool, "Sound rendered on right device"); | |
| fail.remove(); | |
| pass.remove(); | |
| audio.pause(); | |
| e.target.checked = false; | |
| e.target.disabled = true; | |
| t.done(); | |
| }; | |
| pass.style.backgroundColor = "#0f0"; | |
| pass.textContent = "\u2713 Sound plays on " + device.label; | |
| pass.addEventListener("click", result(true)); | |
| fail.style.backgroundColor = "#f00"; | |
| fail.textContent = "\u274C Sound doesn't play on " + device.label; | |
| fail.addEventListener("click", result(true)); | |
| const container = e.target.parentNode.parentNode; | |
| container.appendChild(pass); | |
| container.appendChild(fail); | |
| }), "setSinkId for " + device.label + " resolves"); | |
| }; | |
| const addAuthorizedDevice = (groupId) => { | |
| const device = outputList.find(by_id(groupId)); | |
| const async_t = async_test("Selecting output device " + device.label + " makes the audio rendered on the proper device"); | |
| const li = document.createElement("li"); | |
| const label = document.createElement("label"); | |
| const input = document.createElement("input"); | |
| input.type = "radio"; | |
| input.name = "device"; | |
| input.dataset["groupid"] = device.groupId; | |
| input.addEventListener("change", selectDeviceTester(async_t)); | |
| const span = document.createElement("span"); | |
| span.textContent = device.label; | |
| label.appendChild(input); | |
| label.appendChild(span); | |
| li.appendChild(label); | |
| authorized.appendChild(li); | |
| }; | |
| const authorizeDeviceTester = (t) => (e) => { | |
| const groupId = e.target.dataset["groupid"]; | |
| navigator.mediaDevices.getUserMedia({audio: {groupId}}) | |
| .then( () => { | |
| addAuthorizedDevice(groupId); | |
| t.done(); | |
| }); | |
| }; | |
| promise_test(gum => | |
| navigator.mediaDevices.getUserMedia({audio: true}).then( | |
| () => { | |
| promise_test(t => | |
| navigator.mediaDevices.enumerateDevices().then(list => { | |
| assert_not_equals(list.find(is_output), undefined, "media device list includes at least one audio output device"); | |
| outputList = list.filter(is_output); | |
| outputList.forEach(d => { | |
| const li = document.createElement("li"); | |
| assert_not_equals(d.label, "", "Audio Output Device Labels are available after permission grant"); | |
| li.textContent = d.label; | |
| // Check permission | |
| promise_test(perm => navigator.permissions.query({name: "speaker", deviceId: d.deviceId}).then(({state}) => { | |
| if (state === "granted") { | |
| addAuthorizedDevice(d.groupId); | |
| } else if (state === "prompt") { | |
| const inp = list.find(inp => inp.kind === "audioinput" && inp.groupId === d.groupId); | |
| if (inp || true) { | |
| const async_t = async_test("Authorizing output devices via permission requests for microphones works"); | |
| const button = document.createElement("button"); | |
| button.textContent = "Authorize access"; | |
| button.dataset["groupid"] = d.groupId; | |
| button.addEventListener("click", async_t.step_func_done(authorizeDeviceTester(async_t))); | |
| li.appendChild(button); | |
| } | |
| available.appendChild(li); | |
| } | |
| }, () => { | |
| // if we can't query the permission, we assume it's granted :/ | |
| addAuthorizedDevice(d.groupId); | |
| }) | |
| , "Query permission to use " + d.label); | |
| }); | |
| }), "List media devices"); | |
| }), "Authorize mike access"); | |
| </script> |