browser_selectables.js (16830B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 /* import-globals-from ../../mochitest/role.js */ 8 /* import-globals-from ../../mochitest/states.js */ 9 loadScripts( 10 { name: "role.js", dir: MOCHITESTS_DIR }, 11 { name: "states.js", dir: MOCHITESTS_DIR } 12 ); 13 14 function getSelectedIds(selectable) { 15 return selectable 16 .getAttributeValue("AXSelectedChildren") 17 .map(c => c.getAttributeValue("AXDOMIdentifier")); 18 } 19 20 /** 21 * Test aria tabs 22 */ 23 addAccessibleTask("mac/doc_aria_tabs.html", async (browser, accDoc) => { 24 let tablist = getNativeInterface(accDoc, "tablist"); 25 is( 26 tablist.getAttributeValue("AXRole"), 27 "AXTabGroup", 28 "Correct role for tablist" 29 ); 30 31 let tabMacAccs = tablist.getAttributeValue("AXTabs"); 32 is(tabMacAccs.length, 3, "3 items in AXTabs"); 33 34 let selectedTabs = tablist.getAttributeValue("AXSelectedChildren"); 35 is(selectedTabs.length, 1, "one selected tab"); 36 37 let tab = selectedTabs[0]; 38 is(tab.getAttributeValue("AXRole"), "AXRadioButton", "Correct role for tab"); 39 is( 40 tab.getAttributeValue("AXSubrole"), 41 "AXTabButton", 42 "Correct subrole for tab" 43 ); 44 is(tab.getAttributeValue("AXTitle"), "First Tab", "Correct title for tab"); 45 46 let tabToSelect = tabMacAccs[1]; 47 is( 48 tabToSelect.getAttributeValue("AXTitle"), 49 "Second Tab", 50 "Correct title for tab" 51 ); 52 53 let actions = tabToSelect.actionNames; 54 ok(true, actions); 55 ok(actions.includes("AXPress"), "Has switch action"); 56 57 let evt = waitForMacEvent("AXSelectedChildrenChanged"); 58 tabToSelect.performAction("AXPress"); 59 await evt; 60 61 selectedTabs = tablist.getAttributeValue("AXSelectedChildren"); 62 is(selectedTabs.length, 1, "one selected tab"); 63 is( 64 selectedTabs[0].getAttributeValue("AXTitle"), 65 "Second Tab", 66 "Correct title for tab" 67 ); 68 }); 69 70 addAccessibleTask('<p id="p">hello</p>', async (browser, accDoc) => { 71 let p = getNativeInterface(accDoc, "p"); 72 ok( 73 p.attributeNames.includes("AXSelected"), 74 "html element includes 'AXSelected' attribute" 75 ); 76 is(p.getAttributeValue("AXSelected"), 0, "AX selected is 'false'"); 77 }); 78 79 addAccessibleTask( 80 `<select id="select" aria-label="Choose a number" multiple> 81 <option id="one" selected>One</option> 82 <option id="two">Two</option> 83 <option id="three">Three</option> 84 <option id="four" disabled>Four</option> 85 </select>`, 86 async (browser, accDoc) => { 87 let select = getNativeInterface(accDoc, "select"); 88 let one = getNativeInterface(accDoc, "one"); 89 let two = getNativeInterface(accDoc, "two"); 90 let three = getNativeInterface(accDoc, "three"); 91 let four = getNativeInterface(accDoc, "four"); 92 93 is( 94 select.getAttributeValue("AXDescription"), 95 "Choose a number", 96 "Select titled correctly" 97 ); 98 ok( 99 select.attributeNames.includes("AXOrientation"), 100 "Have orientation attribute" 101 ); 102 ok( 103 select.isAttributeSettable("AXSelectedChildren"), 104 "Select can have AXSelectedChildren set" 105 ); 106 107 is(one.getAttributeValue("AXTitle"), "", "Option should not have a title"); 108 is( 109 one.getAttributeValue("AXValue"), 110 "One", 111 "Option should have correct value" 112 ); 113 is( 114 one.getAttributeValue("AXRole"), 115 "AXStaticText", 116 "Options should have AXStaticText role" 117 ); 118 ok(one.isAttributeSettable("AXSelected"), "Option can have AXSelected set"); 119 120 is(select.getAttributeValue("AXSelectedChildren").length, 1); 121 let evt = waitForMacEvent("AXSelectedChildrenChanged"); 122 one.setAttributeValue("AXSelected", false); 123 await evt; 124 is(select.getAttributeValue("AXSelectedChildren").length, 0); 125 evt = waitForMacEvent("AXSelectedChildrenChanged"); 126 three.setAttributeValue("AXSelected", true); 127 await evt; 128 is(select.getAttributeValue("AXSelectedChildren").length, 1); 129 ok(getSelectedIds(select).includes("three"), "'three' is selected"); 130 evt = waitForMacEvent("AXSelectedChildrenChanged"); 131 select.setAttributeValue("AXSelectedChildren", [one, two]); 132 await evt; 133 await untilCacheOk(() => { 134 let ids = getSelectedIds(select); 135 return ids[0] == "one" && ids[1] == "two"; 136 }, "Got correct selected children"); 137 138 evt = waitForMacEvent("AXSelectedChildrenChanged"); 139 select.setAttributeValue("AXSelectedChildren", [three, two, four]); 140 await evt; 141 await untilCacheOk(() => { 142 let ids = getSelectedIds(select); 143 return ids[0] == "two" && ids[1] == "three"; 144 }, "Got correct selected children"); 145 146 ok(!four.getAttributeValue("AXEnabled"), "Disabled option is disabled"); 147 } 148 ); 149 150 addAccessibleTask( 151 `<select id="select" aria-label="Choose a thing" multiple> 152 <optgroup label="Fruits"> 153 <option id="banana" selected>Banana</option> 154 <option id="apple">Apple</option> 155 <option id="orange">Orange</option> 156 </optgroup> 157 <optgroup label="Vegetables"> 158 <option id="lettuce" selected>Lettuce</option> 159 <option id="tomato">Tomato</option> 160 <option id="onion">Onion</option> 161 </optgroup> 162 <optgroup label="Spices"> 163 <option id="cumin">Cumin</option> 164 <option id="coriander">Coriander</option> 165 <option id="allspice" selected>Allspice</option> 166 </optgroup> 167 <option id="everything">Everything</option> 168 </select>`, 169 async (browser, accDoc) => { 170 let select = getNativeInterface(accDoc, "select"); 171 172 is( 173 select.getAttributeValue("AXDescription"), 174 "Choose a thing", 175 "Select titled correctly" 176 ); 177 ok( 178 select.attributeNames.includes("AXOrientation"), 179 "Have orientation attribute" 180 ); 181 ok( 182 select.isAttributeSettable("AXSelectedChildren"), 183 "Select can have AXSelectedChildren set" 184 ); 185 let childValueSelectablePairs = select 186 .getAttributeValue("AXChildren") 187 .map(c => [ 188 c.getAttributeValue("AXValue"), 189 c.isAttributeSettable("AXSelected"), 190 c.getAttributeValue("AXEnabled"), 191 ]); 192 [ 193 ["", false, 0], 194 ["Fruits", false, 0], 195 ["Banana", true, 1], 196 ["Apple", true, 1], 197 ["Orange", true, 1], 198 ["", false, 0], 199 ["Vegetables", false, 0], 200 ["Lettuce", true, 1], 201 ["Tomato", true, 1], 202 ["Onion", true, 1], 203 ["", false, 0], 204 ["Spices", false, 0], 205 ["Cumin", true, 1], 206 ["Coriander", true, 1], 207 ["Allspice", true, 1], 208 ["Everything", true, 1], 209 ]; 210 Assert.deepEqual( 211 childValueSelectablePairs, 212 [ 213 ["", false, false], 214 ["Fruits", false, false], 215 ["Banana", true, true], 216 ["Apple", true, true], 217 ["Orange", true, true], 218 ["", false, false], 219 ["Vegetables", false, false], 220 ["Lettuce", true, true], 221 ["Tomato", true, true], 222 ["Onion", true, true], 223 ["", false, false], 224 ["Spices", false, false], 225 ["Cumin", true, true], 226 ["Coriander", true, true], 227 ["Allspice", true, true], 228 ["Everything", true, true], 229 ], 230 "Options are selectable, group labels are not" 231 ); 232 233 let allspice = getNativeInterface(accDoc, "allspice"); 234 is( 235 allspice.getAttributeValue("AXTitle"), 236 "", 237 "Option should not have a title" 238 ); 239 is( 240 allspice.getAttributeValue("AXValue"), 241 "Allspice", 242 "Option should have a value" 243 ); 244 is( 245 allspice.getAttributeValue("AXRole"), 246 "AXStaticText", 247 "Options should have AXStaticText role" 248 ); 249 ok( 250 allspice.isAttributeSettable("AXSelected"), 251 "Option can have AXSelected set" 252 ); 253 is( 254 allspice 255 .getAttributeValue("AXParent") 256 .getAttributeValue("AXDOMIdentifier"), 257 "select", 258 "Select is direct parent of nested option" 259 ); 260 261 let groupLabel = select.getAttributeValue("AXChildren")[1]; 262 ok( 263 !groupLabel.isAttributeSettable("AXSelected"), 264 "Group label should not be selectable" 265 ); 266 is( 267 groupLabel.getAttributeValue("AXValue"), 268 "Fruits", 269 "Group label should have a value" 270 ); 271 is( 272 groupLabel.getAttributeValue("AXTitle"), 273 null, 274 "Group label should not have a title" 275 ); 276 is( 277 groupLabel.getAttributeValue("AXRole"), 278 "AXStaticText", 279 "Group label should have AXStaticText role" 280 ); 281 is( 282 groupLabel 283 .getAttributeValue("AXParent") 284 .getAttributeValue("AXDOMIdentifier"), 285 "select", 286 "Select is direct parent of group label" 287 ); 288 289 Assert.deepEqual(getSelectedIds(select), ["banana", "lettuce", "allspice"]); 290 } 291 ); 292 293 addAccessibleTask( 294 `<div role="listbox" id="select" aria-label="Choose a number" aria-multiselectable="true"> 295 <div role="option" id="one" aria-selected="true">One</div> 296 <div role="option" id="two">Two</div> 297 <div role="option" id="three">Three</div> 298 <div role="option" id="four" aria-disabled="true">Four</div> 299 </div>`, 300 async (browser, accDoc) => { 301 let select = getNativeInterface(accDoc, "select"); 302 let one = getNativeInterface(accDoc, "one"); 303 304 is( 305 select.getAttributeValue("AXDescription"), 306 "Choose a number", 307 "Select titled correctly" 308 ); 309 ok( 310 select.attributeNames.includes("AXOrientation"), 311 "Have orientation attribute" 312 ); 313 ok( 314 select.isAttributeSettable("AXSelectedChildren"), 315 "Select can have AXSelectedChildren set" 316 ); 317 318 is(one.getAttributeValue("AXTitle"), "", "Option should not have a title"); 319 is( 320 one.getAttributeValue("AXValue"), 321 "One", 322 "Option should have correct value" 323 ); 324 is( 325 one.getAttributeValue("AXRole"), 326 "AXStaticText", 327 "Options should have AXStaticText role" 328 ); 329 ok(one.isAttributeSettable("AXSelected"), "Option can have AXSelected set"); 330 331 is(select.getAttributeValue("AXSelectedChildren").length, 1); 332 let evt = waitForMacEvent("AXSelectedChildrenChanged"); 333 // Change selection from content. 334 await SpecialPowers.spawn(browser, [], () => { 335 content.document.getElementById("one").removeAttribute("aria-selected"); 336 }); 337 await evt; 338 is(select.getAttributeValue("AXSelectedChildren").length, 0); 339 } 340 ); 341 342 addAccessibleTask( 343 `<div role="listbox" id="select" aria-label="Choose a number"> 344 <div id="groupOne" role="group"> 345 <div role="option" id="one" aria-selected="true">One</div> 346 </div> 347 <div id="groupTwo" role="group"> 348 <div role="option" id="two" aria-selected="false">Two</div> 349 </div> 350 <div id="groupThree" role="group"> 351 <div role="option" id="three" aria-selected="false">Three</div> 352 </div> 353 </div>`, 354 async function testGroupedListbox(browser, accDoc) { 355 let select = getNativeInterface(accDoc, "select"); 356 let one = getNativeInterface(accDoc, "one"); 357 let two = getNativeInterface(accDoc, "two"); 358 359 // Check the listbox 360 is( 361 select.getAttributeValue("AXDescription"), 362 "Choose a number", 363 "Select titled correctly" 364 ); 365 ok( 366 select.isAttributeSettable("AXSelectedChildren"), 367 "Select can have AXSelectedChildren set" 368 ); 369 // Check the first option 370 is( 371 one.getAttributeValue("AXValue"), 372 "One", 373 "First option has correct value" 374 ); 375 is( 376 one.getAttributeValue("AXSelected"), 377 1, 378 "The first option has selected state" 379 ); 380 // Check the second option 381 is( 382 two.getAttributeValue("AXValue"), 383 "Two", 384 "Second option has correct value" 385 ); 386 is( 387 two.getAttributeValue("AXSelected"), 388 0, 389 "The second option has no selected state" 390 ); 391 // Check the relationship between the listbox and its selected children 392 is( 393 select.getAttributeValue("AXSelectedChildren").length, 394 1, 395 "Listbox has one selected child" 396 ); 397 let selectedChild = select.getAttributeValue("AXSelectedChildren")[0]; 398 is( 399 selectedChild.getAttributeValue("AXValue"), 400 "One", 401 "The first option is the selected child" 402 ); 403 404 let evt = waitForMacEvent("AXSelectedChildrenChanged"); 405 // Remove all selection. 406 await SpecialPowers.spawn(browser, [], () => { 407 content.document 408 .getElementById("one") 409 .setAttribute("aria-selected", "false"); 410 }); 411 await evt; 412 // Check the relationship between the listbox and its selected children 413 // after removing all selection. 414 is( 415 select.getAttributeValue("AXSelectedChildren").length, 416 0, 417 "Listbox has no selected child" 418 ); 419 is( 420 one.getAttributeValue("AXSelected"), 421 0, 422 "The first option has no selected state" 423 ); 424 425 evt = waitForMacEvent("AXSelectedChildrenChanged"); 426 // Modify listbox so the second item is selected. 427 await SpecialPowers.spawn(browser, [], () => { 428 content.document 429 .getElementById("two") 430 .setAttribute("aria-selected", "true"); 431 }); 432 await evt; 433 // Check the relationship between the listbox and its selected children 434 // after selecting the second option. 435 is( 436 select.getAttributeValue("AXSelectedChildren").length, 437 1, 438 "Listbox has one selected child" 439 ); 440 selectedChild = select.getAttributeValue("AXSelectedChildren")[0]; 441 is( 442 selectedChild.getAttributeValue("AXValue"), 443 "Two", 444 "The second option is the selected child" 445 ); 446 is( 447 two.getAttributeValue("AXSelected"), 448 1, 449 "The second option has selected state" 450 ); 451 } 452 ); 453 454 addAccessibleTask( 455 ` 456 <div role="listbox" id="listbox"> 457 <div role="group"> 458 <div role="group"> 459 <div role="option" id="optionA" aria-selected="true"> 460 <div role="group"> 461 <button id="one">hi</button> 462 </div> 463 </div> 464 <div role="option" id="optionB" aria-selected="false"> 465 <div role="group"> 466 <button id="two">hello</button> 467 </div> 468 </div> 469 </div> 470 </div> 471 `, 472 async function testMultiGroupedListbox(browser, accDoc) { 473 let listbox = getNativeInterface(accDoc, "listbox"); 474 let optionA = getNativeInterface(accDoc, "optionA"); 475 let optionB = getNativeInterface(accDoc, "optionB"); 476 477 ok( 478 listbox.isAttributeSettable("AXSelectedChildren"), 479 "Select can have AXSelectedChildren set" 480 ); 481 // Check the first option 482 is( 483 optionA.getAttributeValue("AXValue"), 484 "hi", 485 "First option has correct value" 486 ); 487 is( 488 optionA.getAttributeValue("AXSelected"), 489 1, 490 "The first option has selected state" 491 ); 492 // Check the second option 493 is( 494 optionB.getAttributeValue("AXValue"), 495 "hello", 496 "Second option has correct value" 497 ); 498 is( 499 optionB.getAttributeValue("AXSelected"), 500 0, 501 "The second option has no selected state" 502 ); 503 // Check the relationship between the listbox and its selected children 504 is( 505 listbox.getAttributeValue("AXSelectedChildren").length, 506 1, 507 "Listbox has one selected child" 508 ); 509 let selectedChild = listbox.getAttributeValue("AXSelectedChildren")[0]; 510 is( 511 selectedChild.getAttributeValue("AXValue"), 512 "hi", 513 "The first option is the selected child" 514 ); 515 516 let evt = waitForMacEvent("AXSelectedChildrenChanged"); 517 // Remove all selection. 518 await SpecialPowers.spawn(browser, [], () => { 519 content.document 520 .getElementById("optionA") 521 .setAttribute("aria-selected", "false"); 522 }); 523 await evt; 524 // Check the relationship between the listbox and its selected children 525 // after removing all selection. 526 is( 527 listbox.getAttributeValue("AXSelectedChildren").length, 528 0, 529 "Listbox has no selected child" 530 ); 531 is( 532 optionA.getAttributeValue("AXSelected"), 533 0, 534 "The first option has no selected state" 535 ); 536 537 evt = waitForMacEvent("AXSelectedChildrenChanged"); 538 // Modify listbox so the second item is selected. 539 await SpecialPowers.spawn(browser, [], () => { 540 content.document 541 .getElementById("optionB") 542 .setAttribute("aria-selected", "true"); 543 }); 544 await evt; 545 // Check the relationship between the listbox and its selected children 546 // after selecting the second option. 547 is( 548 listbox.getAttributeValue("AXSelectedChildren").length, 549 1, 550 "Listbox has one selected child" 551 ); 552 selectedChild = listbox.getAttributeValue("AXSelectedChildren")[0]; 553 is( 554 selectedChild.getAttributeValue("AXValue"), 555 "hello", 556 "The second option is the selected child" 557 ); 558 is( 559 optionB.getAttributeValue("AXSelected"), 560 1, 561 "The second option has selected state" 562 ); 563 } 564 );