1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
|
<!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 popover 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 popover slot="listbox" behavior="listbox" id="selectMenu1-popover">
<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 popover behavior="listbox" id="selectMenu2-popover">
<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 popover 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 popover 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 popover 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 popover 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 popover 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 popover behavior="listbox" id="selectMenu13-originalListbox">
<option id="selectMenu13-option1">one</option>
<option id="selectMenu13-option2">two</option>
</div>
</div>
<div popover 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 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 selectMenu1 = document.getElementById("selectMenu1");
const selectMenu1Popover = document.getElementById("selectMenu1-popover");
const selectMenu1Button = document.getElementById("selectMenu1-button");
const selectMenu1Child2 = document.getElementById("selectMenu1-child2");
assert_false(selectMenu1Popover.matches(':popover-open'));
selectMenu1Button.click();
assert_false(selectMenu1Popover.matches(':popover-open'), "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(selectMenu1Popover.matches(':popover-open'));
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 selectMenu2Popover = document.getElementById("selectMenu2-popover");
const selectMenu2Button = document.getElementById("selectMenu2-button");
const selectMenu2Child2 = document.getElementById("selectMenu2-child2");
const selectMenu2Child4 = document.getElementById("selectMenu2-child4");
assert_false(selectMenu2Popover.matches(':popover-open'));
await clickOn(selectMenu2Button);
assert_false(selectMenu2Popover.matches(':popover-open'), "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("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(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 popover should no longer be option parts");
assert_true(selectMenu.open, "Clicking non-part options shouldn't close the popover");
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("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);
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>
|