blob: 7a11b57fcb786e78c190ccd39635311b73069ed6 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<title>HTMLSelectMenuElement 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>
<selectmenu id="selectMenu0">
<div popup=popup slot="listbox" behavior="listbox">
<option id="selectMenu0-child1">one</option>
<option id="selectMenu0-child2">two</option>
<div behavior="option" id="selectMenu0-child3">three</div>
</div>
<option id="selectMenu0-child4">four</option>
</selectmenu>
<selectmenu id="selectMenu1">
<div popup=popup slot="listbox" behavior="listbox" id="selectMenu1-popup">
<div behavior="button" id="selectMenu1-button">
Custom button
</div>
<option>one</option>
<option id="selectMenu1-child2">two</option>
</div>
</selectmenu>
<selectmenu id="selectMenu2">
<div slot="button" behavior="button" id="selectMenu2-button">
Custom button
<div popup=popup behavior="listbox" id="selectMenu2-popup">
<option>one</option>
<option id="selectMenu2-child2">two</option>
</div>
</div>
<option>three</option>
<div>
This is some text.
<option id="selectMenu2-child4">four</option>
More text.
</div>
</selectmenu>
<selectmenu id="selectMenu3">
<div slot="button" id="selectMenu3-button-slot">
<div behavior="button" id="selectMenu3-button0">button0</div>
</div>
<option>one</option>
</selectmenu>
<selectmenu id="selectMenu4">
<div slot="button" behavior="button" id="selectMenu4-button0">button0</div>
<div slot="listbox" id="selectMenu4-listbox-slot">
<div popup=popup behavior="listbox" id="selectMenu4-listbox0">
<option>one</option>
<option id="selectMenu4-option2">two</option>
</div>
</div>
</selectmenu>
<selectmenu id="selectMenu5">
<div slot="button" id="selectMenu5-button-slot">
<div behavior="button" id="selectMenu5-button0">button0</div>
<div behavior="selected-value" id="selectMenu5-selectedValue0"></div>
</div>
<option>one</option>
<option id="selectMenu5-option0">two</option>
</selectmenu>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectmenu id="selectMenu6">
<div slot="button"></div>
<div popup=popup slot="listbox" behavior="listbox"></div>
</selectmenu>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectmenu id="selectMenu7">
<div slot="listbox"></div>
<div slot="button" behavior="button"></div>
</selectmenu>
<!-- No associated JS test -- just don't crash when parsing! -->
<selectmenu id="selectMenu8">
<div slot="listbox"></div>
<option>one</option>
</selectmenu>
<selectmenu id="selectMenu9">
<div slot="listbox" id="selectMenu9-listbox-slot">
<div popup=popup behavior="listbox" id="selectMenu9-originalListbox">
<option>one</option>
<option id="selectMenu9-option2">two</option>
</div>
</div>
</selectmenu>
<selectmenu id="selectMenu10">
<option slot="button" id="selectMenu10-slottedOption">Test 10</option>
<option>one</option>
<option id="selectMenu10-option2">two</option>
</selectmenu>
<selectmenu id="selectMenu11">
<div popup=popup slot="listbox" behavior="listbox">
<option>one</option>
</div>
<div slot="button" behavior="listbox" id="selectMenu11-button">Test</div>
</selectmenu>
<selectmenu id="selectMenu12">
<div slot="button" id="selectMenu12-button-slot">
<div behavior="button" id="selectMenu12-button0">button0</div>
</div>
<div slot="listbox" id="selectMenu12-listbox-slot">
<div popup=popup behavior="listbox" id="selectMenu12-originalListbox">
<option id="selectMenu12-option1">one</option>
<option>two</option>
</div>
</div>
</selectmenu>
<selectmenu id="selectMenu13">
<div slot="button" id="selectMenu12-button-slot">
<div id="selectMenu13-removeContent-button">
<div behavior="button" id="selectMenu13-button0">button0</div>
<div behavior="button" id="selectMenu13-button1">button1</div>
</div>
<div behavior="button" id="selectMenu13-button2">button2</div>
</div>
<div slot="listbox" id="selectMenu13-listbox-slot">
<div id="selectMenu13-removeContent-listbox">
<div popup=popup behavior="listbox" id="selectMenu13-originalListbox">
<option id="selectMenu13-option1">one</option>
<option id="selectMenu13-option2">two</option>
</div>
</div>
<div popup=popup behavior="listbox" id="selectMenu13-newListbox">
<option>three</option>
<option id="selectMenu13-option4">four</option>
</div>
</div>
</selectmenu>
<selectmenu id="selectMenu14">
<div slot="button" behavior="button" id="selectMenu14-button0">button0</div>
<option>one</option>
<option id="selectMenu14-option2">two</option>
</selectmenu>
<selectmenu id="selectMenu15">
<div slot="button" id="selectMenu15-div0"></div>
<option>one</option>
</selectmenu>
<selectmenu id="selectMenu16">
<div slot="button">
<div id="selectMenu16-div0">
<div behavior="button" id="selectMenu16-button0">button</div>
</div>
</div>
<option>one</option>
</selectmenu>
<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 selectMenu0 = document.getElementById("selectMenu0");
const selectMenu0Child1 = document.getElementById("selectMenu0-child1");
const selectMenu0Child2 = document.getElementById("selectMenu0-child2");
const selectMenu0Child3 = document.getElementById("selectMenu0-child3");
assert_equals(selectMenu0.value, "one");
await clickOn(selectMenu0);
await clickOn(selectMenu0Child2);
assert_equals(selectMenu0.value, "two");
await clickOn(selectMenu0);
await clickOn(selectMenu0Child3);
assert_equals(selectMenu0.value, "two", "Clicking a non-HTMLOptionElement labeled as an option should do nothing");
await clickOn(selectMenu0Child1);
assert_equals(selectMenu0.value, "one");
assert_false(selectMenu0.open);
}, "HTMLOptionElements (and not other element types) should receive option controller code");
promise_test(async () => {
const selectMenu0 = document.getElementById("selectMenu0");
const selectMenu0Child4 = document.getElementById("selectMenu0-child4");
assert_equals(selectMenu0.value, "one");
await clickOn(selectMenu0);
assert_true(selectMenu0.open);
selectMenu0Child4.click();
assert_equals(selectMenu0.value, "one", "Clicking an option outside of the popup 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 selectMenu1 = document.getElementById("selectMenu1");
const selectMenu1Popup = document.getElementById("selectMenu1-popup");
const selectMenu1Button = document.getElementById("selectMenu1-button");
const selectMenu1Child2 = document.getElementById("selectMenu1-child2");
assert_false(selectMenu1Popup.matches(':top-layer'));
selectMenu1Button.click();
assert_false(selectMenu1Popup.matches(':top-layer'), "Clicking a button part that is a descendant of the listbox part should have no effect");
assert_equals(selectMenu1.value, "one");
await clickOn(selectMenu1);
assert_true(selectMenu1Popup.matches(':top-layer'));
await clickOn(selectMenu1Child2);
assert_equals(selectMenu1.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 selectMenu2 = document.getElementById("selectMenu2");
const selectMenu2Popup = document.getElementById("selectMenu2-popup");
const selectMenu2Button = document.getElementById("selectMenu2-button");
const selectMenu2Child2 = document.getElementById("selectMenu2-child2");
const selectMenu2Child4 = document.getElementById("selectMenu2-child4");
assert_false(selectMenu2Popup.matches(':top-layer'));
await clickOn(selectMenu2Button);
assert_false(selectMenu2Popup.matches(':top-layer'), "Clicking a button part should not show an invalid listbox part");
assert_equals(selectMenu2.value, "three");
await clickOn(selectMenu2Button);
await clickOn(selectMenu2Child4);
assert_equals(selectMenu2.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 selectMenu3 = document.getElementById("selectMenu3");
const selectMenu3ButtonSlot = document.getElementById("selectMenu3-button-slot");
const selectMenu3Button0 = document.getElementById("selectMenu3-button0");
assert_false(selectMenu3.open);
let button1 = document.createElement("div");
button1.innerText = "button1";
button1.setAttribute("behavior", "button");
selectMenu3ButtonSlot.appendChild(button1);
await clickOn(button1);
assert_false(selectMenu3.open, "A button part should only get controller code if it's first in document order, even if added dynamically");
await clickOn(selectMenu3Button0);
assert_true(selectMenu3.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 selectMenu4 = document.getElementById("selectMenu4");
const selectMenu4Button0 = document.getElementById("selectMenu4-button0");
const selectMenu4ListboxSlot = document.getElementById("selectMenu4-listbox-slot");
const selectMenu4Option2 = document.getElementById("selectMenu4-option2");
assert_false(selectMenu4.open);
let listbox2 = document.createElement("div");
listbox2.innerHTML = `
<option>three</option>
<option id="selectMenu4-option4">four</option>
`;
listbox2.setAttribute("behavior", "listbox");
selectMenu4ListboxSlot.appendChild(listbox2);
await clickOn(selectMenu4Button0);
assert_true(selectMenu4.open);
const selectMenu4Option4 = document.getElementById("selectMenu4-option4");
await clickOn(selectMenu4Option4);
assert_equals(selectMenu3.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(selectMenu4Button0);
assert_true(selectMenu4.open);
await clickOn(selectMenu4Option2);
assert_equals(selectMenu4.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 selectMenu5 = document.getElementById("selectMenu5");
const selectMenu5ButtonSlot = document.getElementById("selectMenu5-button-slot");
const selectMenu5Button0 = document.getElementById("selectMenu5-button0");
const selectMenu5SelectedValue0 = document.getElementById("selectMenu5-selectedValue0");
assert_false(selectMenu3.open);
assert_equals(selectMenu5SelectedValue0.innerText, "one");
let selectedValue1 = document.createElement("div");
selectMenu5ButtonSlot.appendChild(selectedValue1);
await clickOn(selectMenu5Button0);
assert_true(selectMenu5.open);
await clickOn(document.getElementById("selectMenu5-option0"));
assert_false(selectMenu5.open);
assert_equals(selectMenu5SelectedValue0.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 selectMenu = document.getElementById("selectMenu9");
const originalListbox = document.getElementById("selectMenu9-originalListbox");
const option2 = document.getElementById("selectMenu9-option2");
assert_equals(selectMenu.value, "one", "Initial value should be the first option");
let newListbox = document.createElement("div");
newListbox.setAttribute("popup", "popup");
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(selectMenu);
assert_true(selectMenu.open, "Menu should open when clicked");
option2.click(); // clickOn doesn't work because the old options are not displayed
assert_equals(selectMenu.value, "three", "Elements in second popup should no longer be option parts");
assert_true(selectMenu.open, "Clicking non-part options shouldn't close the popup");
await clickOn(newOption2);
assert_false(selectMenu.open);
assert_equals(selectMenu.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 selectMenu = document.getElementById("selectMenu10");
const selectMenu10SlottedOption = document.getElementById("selectMenu10-slottedOption");
await clickOn(selectMenu10SlottedOption);
assert_false(selectMenu.open, "Controller code not applied due to part attribute missing");
selectMenu10SlottedOption.setAttribute("behavior", "button");
await clickOn(selectMenu10SlottedOption);
assert_true(selectMenu.open);
const option2 = document.getElementById("selectMenu10-option2");
await clickOn(option2);
assert_equals(selectMenu.value, "two");
assert_false(selectMenu.open);
selectMenu10SlottedOption.slot = "";
await clickOn(selectMenu);
assert_true(selectMenu.open, "Default button part should be used");
await clickOn(selectMenu10SlottedOption);
assert_equals(selectMenu.value, "Test 10");
}, "Ensure that controller code is applied after updating the slot attribute");
promise_test(async () => {
const selectMenu = document.getElementById("selectMenu11");
const selectMenu11Button= document.getElementById("selectMenu11-button");
await clickOn(selectMenu11Button);
assert_false(selectMenu.open, "Controller code not applied due to part attribute not being button");
selectMenu11Button.remove();
await clickOn(selectMenu);
assert_true(selectMenu.open, "Default button part should be used");
}, "Ensure that controller code is applied when slot and part attributes are different");
promise_test(async () => {
const selectMenu = document.getElementById("selectMenu12");
const originalListbox = document.getElementById("selectMenu12-originalListbox");
assert_equals(selectMenu.value, "one", "Initial value should be the first option");
const selectMenuButtonSlot = document.getElementById("selectMenu12-button-slot");
const selectMenuButton0 = document.getElementById("selectMenu12-button0");
const selectMenuOption1 = document.getElementById("selectMenu12-option1");
assert_false(selectMenu.open);
let button1 = document.createElement("div");
button1.innerText = "button1";
button1.setAttribute("behavior", "button");
selectMenuButtonSlot.insertBefore(button1, selectMenuButton0);
button1.click();
assert_true(selectMenu.open, "Controller code should be applied to the new first button in document order");
await clickOn(selectMenuOption1);
assert_false(selectMenu.open);
selectMenuButton0.click();
assert_false(selectMenu.open);
let button2 = document.createElement("div");
button2.innerText = "button2";
selectMenuButtonSlot.insertBefore(button2, button1);
button2.click();
assert_false(selectMenu.open, "Controller code should not be applied to button2 since it doesn't have behavior attribute set");
button2.setAttribute("behavior", "button");
button2.click();
assert_true(selectMenu.open, "Controller code should be applied to the new button part");
await clickOn(selectMenuOption1);
assert_false(selectMenu.open);
let newListbox = document.createElement("div");
newListbox.setAttribute("popup", "popup");
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);
assert_equals(selectMenu.value, "three", "New value should be the first option");
newListbox.innerHTML = "<option>five</option><option>six</option>";
assert_equals(selectMenu.value, "five", "New value should be the first option");
selectMenu.innerHTML = "<option>seven</option><option id='selectMenu12-option2'>eight</option>";
assert_equals(selectMenu.value, "seven", "New value should be the first option");
const selectMenuOption2 = document.getElementById("selectMenu12-option2");
await clickOn(selectMenu);
assert_true(selectMenu.open);
await clickOn(selectMenuOption2);
assert_equals(selectMenu.value, "eight", "Controller code should be applied to new options");
selectMenuOption2.slot = "button";
assert_equals(selectMenu.value, "seven", "Previous selected option should become invalid");
}, "Ensure that controller code is synchronously applied");
promise_test(async () => {
const selectMenu = document.getElementById("selectMenu13");
assert_equals(selectMenu.value, "one");
const selectMenuButton0 = document.getElementById("selectMenu13-button0");
const selectMenuButton1 = document.getElementById("selectMenu13-button1");
selectMenuButton1.click();
assert_false(selectMenu.open);
selectMenuButton0.click();
assert_true(selectMenu.open, "First button should receive controller code");
await clickOn(document.getElementById("selectMenu13-option2"));
assert_equals(selectMenu.value, "two");
let divButtonToRemove = document.getElementById("selectMenu13-removeContent-button");
divButtonToRemove.innerHTML = "";
selectMenuButton0.click();
assert_false(selectMenu.open, "The first button is invalid");
const selectMenuButton2 = document.getElementById("selectMenu13-button2");
selectMenuButton2.click();
assert_true(selectMenu.open, "The button part should be updated")
await clickOn(document.getElementById("selectMenu13-option1"));
assert_equals(selectMenu.value, "one");
const selectMenuOption4 = document.getElementById("selectMenu13-option4");
selectMenuOption4.click();
assert_equals(selectMenu.value, "one");
let divListboxToRemove = document.getElementById("selectMenu13-removeContent-listbox");
divListboxToRemove.innerHTML = "";
assert_equals(selectMenu.value, "three", "The listbox part should be updated");
selectMenuOption4.click();
assert_equals(selectMenu.value, "four", "Controller code should be applied to the new options");
let selectMenuNewListbox = document.getElementById("selectMenu13-newListbox");
selectMenuNewListbox.innerHTML = "";
assert_equals(selectMenu.value, "");
selectMenuOption4.click();
assert_equals(selectMenu.value, "");
}, "Controller code should be updated when nested parts are removed");
promise_test(async () => {
let selectMenu = document.getElementById("selectMenu14");
assert_equals(selectMenu.value, "one");
const selectMenuButton0 = document.getElementById("selectMenu14-button0");
const selectMenuOption2 = document.getElementById("selectMenu14-option2");
selectMenuButton0.click();
assert_true(selectMenu.open);
await clickOn(selectMenuOption2);
assert_equals(selectMenu.value, "two");
document.body.removeChild(selectMenu);
selectMenu.removeChild(selectMenuOption2);
assert_equals(selectMenu.value, "one");
let newOption = document.createElement("option");
newOption.innerText = "three";
selectMenu.appendChild(newOption);
newOption.click();
assert_equals(selectMenu.value, "three", "New option should receive controller code");
let doc = document.implementation.createHTMLDocument('');
let selectMenu1 = doc.createElement('selectmenu');
let firstOption = doc.createElement('option');
firstOption.innerText = 'one';
let secondOption = doc.createElement('option');
secondOption.innerText = 'two';
selectMenu1.appendChild(firstOption);
selectMenu1.appendChild(secondOption);
assert_equals(selectMenu1.value, "one");
secondOption.click();
assert_equals(selectMenu1.value, "two");
document.body.appendChild(selectMenu1);
selectMenu1.removeChild(secondOption);
assert_equals(selectMenu1.value, "one");
}, "Moving a selectmenu between documents should keep controller code active");
promise_test(async () => {
const selectMenu = document.getElementById("selectMenu15");
const selectMenuButtonContainer = document.getElementById("selectMenu15-div0");
const outerDiv = document.createElement("div");
const button = document.createElement("input");
button.type = button.value = "button";
button.setAttribute("behavior", "button");
outerDiv.appendChild(button);
selectMenuButtonContainer.appendChild(outerDiv);
await clickOn(selectMenu);
assert_true(selectMenu.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 selectMenu = document.getElementById("selectMenu16");
const selectMenuButtonContainer = document.getElementById("selectMenu16-div0");
const selectMenuButton = document.getElementById("selectMenu16-button0");
selectMenuButtonContainer.remove();
selectMenuButton.click();
assert_false(selectMenu.open, "Removed button should no longer have controller code");
}, "Part removals should be detected even when in the subtree of a removed node");
</script>