| <!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 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 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 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 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 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 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 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 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 behavior="listbox" id="selectMenu13-originalListbox"> |
| <option id="selectMenu13-option1">one</option> |
| <option id="selectMenu13-option2">two</option> |
| </div> |
| </div> |
| <div 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", "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(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", "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); |
| 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> |