Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<html lang="en">
<title>HTMLSelectListElement Test: part structure</title>
<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<selectlist id="selectList0">
<div popover slot="listbox" behavior="listbox">
<option id="selectList0-child1">one</option>
<option id="selectList0-child2">two</option>
<div behavior="option" id="selectList0-child3">three</div>
</div>
<option id="selectList0-child4">four</option>
</selectlist>
<selectlist id="selectList1">
<listbox id="selectList1-popover">
<button type=selectlist id="selectList1-button">
Custom button
</button>
<option>one</option>
<option id="selectList1-child2">two</option>
</listbox>
</selectlist>
<selectlist id="selectList2">
<button type=selectlist id="selectList2-button">
Custom button
<listbox id="selectList2-popover">
Custom listbox
</listbox>
</button>
<option>three</option>
<div>
This is some text.
<option id="selectList2-child4">four</option>
More text.
</div>
</selectlist>
<selectlist id="selectList3">
<div slot="button" id="selectList3-button-slot">
<button type=selectlist id="selectList3-button0">button0</button>
</div>
<option>one</option>
</selectlist>
<selectlist id="selectList4">
<button type=selectlist id="selectList4-button0">button0</button>
<div slot="listbox" id="selectList4-listbox-slot">
<div popover behavior="listbox" id="selectList4-listbox0">
<option>one</option>
<option id="selectList4-option2">two</option>
</div>
</div>
</selectlist>
<selectlist id="selectList5">
<div slot="button" id="selectList5-button-slot">
<button type=selectlist id="selectList5-button0">button0</button>
<div behavior="selected-value" id="selectList5-selectedValue0"></div>
</div>
<option>one</option>
<option id="selectList5-option0">two</option>
</selectlist>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectlist id="selectList6">
<div slot="button"></div>
<div popover slot="listbox" behavior="listbox"></div>
</selectlist>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectlist id="selectList7">
<div slot="listbox"></div>
<button type=selectlist></button>
</selectlist>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectlist id="selectList8">
<div slot="listbox"></div>
<option>one</option>
</selectlist>
<selectlist id="selectList9">
<div slot="listbox" id="selectList9-listbox-slot">
<div popover behavior="listbox" id="selectList9-originalListbox">
<option>one</option>
<option id="selectList9-option2">two</option>
</div>
</div>
</selectlist>
<selectlist id="selectList11">
<div popover slot="listbox" behavior="listbox">
<option>one</option>
</div>
<div slot="button" behavior="listbox" id="selectList11-button">Test</div>
</selectlist>
<selectlist id="selectList12">
<button type=selectlist id="selectList12-button0">button0</button>
<listbox id="selectList12-listbox">
<option id="selectList12-option1">one</option>
<option>two</option>
</listbox>
</selectlist>
<selectlist id="selectList13">
<div slot="button" id="selectList12-button-slot">
<div id="selectList13-removeContent-button">
<button type=selectlist id="selectList13-button0">button0</button>
<button type=selectlist id="selectList13-button1">button1</button>
</div>
<button type=selectlist id="selectList13-button2">button2</button>
</div>
<div slot="listbox" id="selectList13-listbox-slot">
<div id="selectList13-removeContent-listbox">
<div popover behavior="listbox" id="selectList13-originalListbox">
<option id="selectList13-option1">one</option>
<option id="selectList13-option2">two</option>
</div>
</div>
<div popover behavior="listbox" id="selectList13-newListbox">
<option>three</option>
<option id="selectList13-option4">four</option>
</div>
</div>
</selectlist>
<selectlist id="selectList14">
<button type=selectlist id="selectList14-button0">button0</button>
<option>one</option>
<option id="selectList14-option2">two</option>
</selectlist>
<selectlist id="selectList15">
<div slot="button" id="selectList15-div0"></div>
<option>one</option>
</selectlist>
<selectlist id="selectList16">
<div slot="button">
<div id="selectList16-div0">
<button type=selectlist id="selectList16-button0">button</button>
</div>
</div>
<option>one</option>
</selectlist>
<script>
function clickOn(element) {
const actions = new test_driver.Actions();
return actions.pointerMove(0, 0, {origin: element})
.pointerDown({button: actions.ButtonType.LEFT})
.pointerUp({button: actions.ButtonType.LEFT})
.send();
}
promise_test(async () => {
const selectList0 = document.getElementById("selectList0");
const selectList0Child1 = document.getElementById("selectList0-child1");
const selectList0Child2 = document.getElementById("selectList0-child2");
const selectList0Child3 = document.getElementById("selectList0-child3");
assert_equals(selectList0.value, "one");
await clickOn(selectList0);
await clickOn(selectList0Child2);
assert_equals(selectList0.value, "two");
await clickOn(selectList0);
await clickOn(selectList0Child3);
assert_equals(selectList0.value, "two", "Clicking a non-HTMLOptionElement labeled as an option should do nothing");
await clickOn(selectList0Child1);
assert_equals(selectList0.value, "one");
assert_false(selectList0.open);
}, "HTMLOptionElements (and not other element types) should receive option controller code");
promise_test(async () => {
const selectList0 = document.getElementById("selectList0");
const selectList0Child4 = document.getElementById("selectList0-child4");
assert_equals(selectList0.value, "one");
await clickOn(selectList0);
assert_true(selectList0.open);
selectList0Child4.click();
assert_equals(selectList0.value, "one", "Clicking an option outside of the popover should not change the value");
}, "To receive option part controller code, an option must be a descendant of the listbox part in a flat tree traversal");
promise_test(async () => {
const selectList1 = document.getElementById("selectList1");
const selectList1Popover = document.getElementById("selectList1-popover");
const selectList1Button = document.getElementById("selectList1-button");
const selectList1Child2 = document.getElementById("selectList1-child2");
assert_false(selectList1Popover.matches(':popover-open'));
selectList1Button.click();
assert_false(selectList1Popover.matches(':popover-open'), "Clicking a button part that is a descendant of the listbox part should have no effect");
assert_equals(selectList1.value, "one");
await clickOn(selectList1);
assert_true(selectList1Popover.matches(':popover-open'));
await clickOn(selectList1Child2);
assert_equals(selectList1.value, "two", "Clicking an <option> should change the value");
}, "To receive button part controller code, an element labeled as a button must not be a descendant of the listbox part in a flat tree traversal");
promise_test(async () => {
const selectList2 = document.getElementById("selectList2");
const selectList2Popover = document.getElementById("selectList2-popover");
const selectList2Button = document.getElementById("selectList2-button");
const selectList2Child2 = document.getElementById("selectList2-child2");
const selectList2Child4 = document.getElementById("selectList2-child4");
assert_false(selectList2Popover.matches(':popover-open'));
await clickOn(selectList2Button);
assert_false(selectList2Popover.matches(':popover-open'), "Clicking a button part should not show an invalid listbox part");
assert_equals(selectList2.value, "three");
await clickOn(selectList2Child4);
assert_equals(selectList2.value, "four", "Clicking an <option> that is a descendant of a valid listbox part should update the value");
}, "To receive listbox part controller code, an element labeled as a listbox must not be a descendant of the button part in a flat tree traversal");
promise_test(async () => {
const selectList3 = document.getElementById("selectList3");
const selectList3ButtonSlot = document.getElementById("selectList3-button-slot");
const selectList3Button0 = document.getElementById("selectList3-button0");
assert_false(selectList3.open);
let button1 = document.createElement("div");
button1.innerText = "button1";
button1.setAttribute("behavior", "button");
selectList3ButtonSlot.appendChild(button1);
await clickOn(button1);
assert_false(selectList3.open, "A button part should only get controller code if it's first in document order, even if added dynamically");
await clickOn(selectList3Button0);
assert_true(selectList3.open, "A button part should get controller code if it's first in document order");
}, "Button controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
promise_test(async () => {
const selectList4 = document.getElementById("selectList4");
const selectList4Button0 = document.getElementById("selectList4-button0");
const selectList4ListboxSlot = document.getElementById("selectList4-listbox-slot");
const selectList4Option2 = document.getElementById("selectList4-option2");
assert_false(selectList4.open);
let listbox2 = document.createElement("div");
listbox2.innerHTML = `
<option>three</option>
<option id="selectList4-option4">four</option>
`;
listbox2.setAttribute("behavior", "listbox");
selectList4ListboxSlot.appendChild(listbox2);
await clickOn(selectList4Button0);
assert_true(selectList4.open);
const selectList4Option4 = document.getElementById("selectList4-option4");
await clickOn(selectList4Option4);
assert_equals(selectList3.value, "one", "An option in a listbox should not get controller code if its listbox isn't first in document order, even if added dynamically");
await clickOn(selectList4Button0);
assert_true(selectList4.open);
await clickOn(selectList4Option2);
assert_equals(selectList4.value, "two", "An option in a listbox should get controller code if its listbox is first in document order, even if another listbox was added dynamically");
}, "Listbox controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
promise_test(async () => {
const selectList5 = document.getElementById("selectList5");
const selectList5ButtonSlot = document.getElementById("selectList5-button-slot");
const selectList5Button0 = document.getElementById("selectList5-button0");
const selectList5SelectedValue0 = document.getElementById("selectList5-selectedValue0");
assert_false(selectList3.open);
assert_equals(selectList5SelectedValue0.innerText, "one");
let selectedValue1 = document.createElement("div");
selectList5ButtonSlot.appendChild(selectedValue1);
await clickOn(selectList5Button0);
assert_true(selectList5.open);
await clickOn(document.getElementById("selectList5-option0"));
assert_false(selectList5.open);
assert_equals(selectList5SelectedValue0.innerText, "two", "first selected-value part in flat tree order should get controller code");
assert_equals(selectedValue1.innerText, "", "Dynamically inserted selected-value part shouldn't get controller code if it's not first in flat tree order");
}, "selected-value controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
promise_test(async () => {
const selectList = document.getElementById("selectList9");
const originalListbox = document.getElementById("selectList9-originalListbox");
const option2 = document.getElementById("selectList9-option2");
assert_equals(selectList.value, "one", "Initial value should be the first option");
let newListbox = document.createElement("div");
newListbox.setAttribute("popover", "auto");
newListbox.setAttribute("behavior", "listbox");
let newOption = document.createElement("option");
newOption.innerText = "three";
newListbox.appendChild(newOption);
let newOption2 = document.createElement("option");
newOption2.innerText = "four";
newListbox.appendChild(newOption2);
originalListbox.parentElement.insertBefore(newListbox, originalListbox);
await clickOn(selectList);
assert_true(selectList.open, "Menu should open when clicked");
option2.click(); // clickOn doesn't work because the old options are not displayed
assert_equals(selectList.value, "three", "Elements in second popover should no longer be option parts");
assert_true(selectList.open, "Clicking non-part options shouldn't close the popover");
await clickOn(newOption2);
assert_false(selectList.open);
assert_equals(selectList.value, "four", "New options should get controller code after listbox switch");
}, "Ensure that option controller code is updated when listbox changes");
promise_test(async () => {
const selectList = document.getElementById("selectList11");
const selectList11Button= document.getElementById("selectList11-button");
await clickOn(selectList11Button);
assert_false(selectList.open, "Controller code not applied due to part attribute not being button");
selectList11Button.remove();
await clickOn(selectList);
assert_true(selectList.open, "Default button part should be used");
}, "Ensure that controller code is applied when slot and part attributes are different");
promise_test(async () => {
const selectList = document.getElementById("selectList12");
const originalListbox = document.getElementById("selectList12-listbox");
assert_equals(selectList.value, "one", "Initial value should be the first option");
const selectListButton0 = document.getElementById("selectList12-button0");
const selectListOption1 = document.getElementById("selectList12-option1");
assert_false(selectList.open);
let button1 = document.createElement("button");
button1.innerText = "button1";
button1.setAttribute("type", "selectlist");
selectList.insertBefore(button1, selectListButton0);
button1.click();
assert_true(selectList.open, "Controller code should be applied to the new first button in document order");
await clickOn(selectListOption1);
assert_false(selectList.open, "listbox should close after clicking option1.");
selectListButton0.click();
assert_true(selectList.open, "listbox should open after clicking button0.");
selectListButton0.click();
assert_false(selectList.open, "listbox should close after clicking button0 again.");
let button2 = document.createElement("button");
button2.innerText = "button2";
selectList.insertBefore(button2, button1);
button2.click();
assert_false(selectList.open, "Controller code should not be applied to button2 since it doesn't have type=selectlist.");
button2.setAttribute("type", "selectlist");
button2.click();
assert_true(selectList.open, "Controller code should be applied to the new button part");
await clickOn(selectListOption1);
assert_false(selectList.open);
let newListbox = document.createElement("listbox");
let newOption = document.createElement("option");
newOption.innerText = "three";
newListbox.appendChild(newOption);
let newOption2 = document.createElement("option");
newOption2.innerText = "four";
newListbox.appendChild(newOption2);
originalListbox.parentElement.insertBefore(newListbox, originalListbox);
assert_equals(selectList.value, "three", "New value should be the first option");
newListbox.innerHTML = "<option>five</option><option>six</option>";
assert_equals(selectList.value, "five", "New value should be the first option");
selectList.innerHTML = "<option>seven</option><option id='selectList12-option2'>eight</option>";
assert_equals(selectList.value, "seven", "New value should be the first option");
const selectListOption2 = document.getElementById("selectList12-option2");
await clickOn(selectList);
assert_true(selectList.open);
await clickOn(selectListOption2);
assert_equals(selectList.value, "eight", "Controller code should be applied to new options");
selectListOption2.slot = "button";
assert_equals(selectList.value, "seven", "Previous selected option should become invalid");
}, "Ensure that controller code is synchronously applied");
promise_test(async () => {
const selectList = document.getElementById("selectList13");
assert_equals(selectList.value, "one");
const selectListButton0 = document.getElementById("selectList13-button0");
const selectListButton1 = document.getElementById("selectList13-button1");
selectListButton1.click();
assert_false(selectList.open);
selectListButton0.click();
assert_true(selectList.open, "First button should receive controller code");
await clickOn(document.getElementById("selectList13-option2"));
assert_equals(selectList.value, "two");
let divButtonToRemove = document.getElementById("selectList13-removeContent-button");
divButtonToRemove.innerHTML = "";
selectListButton0.click();
assert_false(selectList.open, "The first button is invalid");
const selectListButton2 = document.getElementById("selectList13-button2");
selectListButton2.click();
assert_true(selectList.open, "The button part should be updated")
await clickOn(document.getElementById("selectList13-option1"));
assert_equals(selectList.value, "one");
const selectListOption4 = document.getElementById("selectList13-option4");
selectListOption4.click();
assert_equals(selectList.value, "one");
let divListboxToRemove = document.getElementById("selectList13-removeContent-listbox");
divListboxToRemove.innerHTML = "";
assert_equals(selectList.value, "three", "The listbox part should be updated");
selectListOption4.click();
assert_equals(selectList.value, "four", "Controller code should be applied to the new options");
let selectListNewListbox = document.getElementById("selectList13-newListbox");
selectListNewListbox.innerHTML = "";
assert_equals(selectList.value, "");
selectListOption4.click();
assert_equals(selectList.value, "");
}, "Controller code should be updated when nested parts are removed");
promise_test(async () => {
let selectList = document.getElementById("selectList14");
assert_equals(selectList.value, "one");
const selectListButton0 = document.getElementById("selectList14-button0");
const selectListOption2 = document.getElementById("selectList14-option2");
selectListButton0.click();
assert_true(selectList.open);
await clickOn(selectListOption2);
assert_equals(selectList.value, "two");
document.body.removeChild(selectList);
selectList.removeChild(selectListOption2);
assert_equals(selectList.value, "one");
let newOption = document.createElement("option");
newOption.innerText = "three";
selectList.appendChild(newOption);
newOption.click();
assert_equals(selectList.value, "three", "New option should receive controller code");
let doc = document.implementation.createHTMLDocument('');
let selectList1 = doc.createElement('selectlist');
let firstOption = doc.createElement('option');
firstOption.innerText = 'one';
let secondOption = doc.createElement('option');
secondOption.innerText = 'two';
selectList1.appendChild(firstOption);
selectList1.appendChild(secondOption);
assert_equals(selectList1.value, "one");
secondOption.click();
assert_equals(selectList1.value, "two");
document.body.appendChild(selectList1);
selectList1.removeChild(secondOption);
assert_equals(selectList1.value, "one");
}, "Moving a selectlist between documents should keep controller code active");
promise_test(async () => {
const selectList = document.getElementById("selectList15");
const selectListButtonContainer = document.getElementById("selectList15-div0");
const outerDiv = document.createElement("div");
const button = document.createElement("input");
button.type = button.value = "button";
button.setAttribute("behavior", "button");
outerDiv.appendChild(button);
selectListButtonContainer.appendChild(outerDiv);
await clickOn(selectList);
assert_true(selectList.open, "New button should receive controller code");
}, "New parts should be detected even when in the subtree of an inserted node");
promise_test(async () => {
const selectList = document.getElementById("selectList16");
const selectListButtonContainer = document.getElementById("selectList16-div0");
const selectListButton = document.getElementById("selectList16-button0");
selectListButtonContainer.remove();
selectListButton.click();
assert_false(selectList.open, "Removed button should no longer have controller code");
}, "Part removals should be detected even when in the subtree of a removed node");
</script>