browser_outline.js (15904B)
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/states.js */ 8 loadScripts({ name: "states.js", dir: MOCHITESTS_DIR }); 9 10 /** 11 * Test outline, outline rows with computed properties 12 */ 13 addAccessibleTask( 14 ` 15 <h3 id="tree1"> 16 Foods 17 </h3> 18 <ul role="tree" aria-labelledby="tree1" id="outline"> 19 <li role="treeitem" aria-expanded="false"> 20 <span> 21 Fruits 22 </span> 23 <ul> 24 <li role="none">Oranges</li> 25 <li role="treeitem" aria-expanded="true"> 26 <span> 27 Apples 28 </span> 29 <ul role="group"> 30 <li role="none">Honeycrisp</li> 31 <li role="none">Granny Smith</li> 32 </ul> 33 </li> 34 </ul> 35 </li> 36 <li id="vegetables" role="treeitem" aria-expanded="false"> 37 <span> 38 Vegetables 39 </span> 40 <ul role="group"> 41 <li role="treeitem" aria-expanded="true"> 42 <span> 43 Podded Vegetables 44 </span> 45 <ul role="group"> 46 <li role="none">Lentil</li> 47 <li role="none">Pea</li> 48 </ul> 49 </li> 50 </ul> 51 </li> 52 </ul> 53 `, 54 async (browser, accDoc) => { 55 const outline = getNativeInterface(accDoc, "outline"); 56 is( 57 outline.getAttributeValue("AXRole"), 58 "AXOutline", 59 "Correct role for outline" 60 ); 61 62 const outChildren = outline.getAttributeValue("AXChildren"); 63 is(outChildren.length, 2, "Outline has two direct children"); 64 is(outChildren[0].getAttributeValue("AXSubrole"), "AXOutlineRow"); 65 is(outChildren[1].getAttributeValue("AXSubrole"), "AXOutlineRow"); 66 67 const outRows = outline.getAttributeValue("AXRows"); 68 is(outRows.length, 4, "Outline has four rows"); 69 is( 70 outRows[0].getAttributeValue("AXDisclosing"), 71 0, 72 "Row is not disclosing" 73 ); 74 is( 75 outRows[0].getAttributeValue("AXDisclosedByRow"), 76 null, 77 "Row is direct child of outline" 78 ); 79 is( 80 outRows[0].getAttributeValue("AXDisclosedRows").length, 81 0, 82 "Row has no row children, only group" 83 ); 84 is( 85 outRows[0].getAttributeValue("AXDisclosureLevel"), 86 0, 87 "Row is level zero" 88 ); 89 90 is(outRows[1].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); 91 is( 92 outRows[1].getAttributeValue("AXDisclosedByRow"), 93 null, 94 "Row is direct child of group" 95 ); 96 is( 97 outRows[1].getAttributeValue("AXDisclosedRows").length, 98 0, 99 "Row has no row children" 100 ); 101 is( 102 outRows[1].getAttributeValue("AXDisclosureLevel"), 103 0, 104 "Row is level zero" 105 ); 106 107 is( 108 outRows[2].getAttributeValue("AXDisclosing"), 109 0, 110 "Row is not disclosing" 111 ); 112 is( 113 outRows[2].getAttributeValue("AXDisclosedByRow"), 114 null, 115 "Row is direct child of outline" 116 ); 117 is( 118 outRows[2].getAttributeValue("AXDisclosedRows").length, 119 1, 120 "Row has one row child" 121 ); 122 is( 123 outRows[2].getAttributeValue("AXDisclosureLevel"), 124 0, 125 "Row is level zero" 126 ); 127 128 is(outRows[3].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); 129 is( 130 outRows[3] 131 .getAttributeValue("AXDisclosedByRow") 132 .getAttributeValue("AXDescription"), 133 outRows[2].getAttributeValue("AXDescription"), 134 "Row is direct child of row[2]" 135 ); 136 is( 137 outRows[3].getAttributeValue("AXDisclosedRows").length, 138 0, 139 "Row has no row children" 140 ); 141 is( 142 outRows[3].getAttributeValue("AXDisclosureLevel"), 143 1, 144 "Row is level one" 145 ); 146 147 let evt = waitForMacEvent("AXRowExpanded", "vegetables"); 148 await SpecialPowers.spawn(browser, [], () => { 149 content.document 150 .getElementById("vegetables") 151 .setAttribute("aria-expanded", "true"); 152 }); 153 await evt; 154 is( 155 outRows[2].getAttributeValue("AXDisclosing"), 156 1, 157 "Row is disclosing after being expanded" 158 ); 159 160 evt = waitForMacEvent("AXRowCollapsed", "vegetables"); 161 await SpecialPowers.spawn(browser, [], () => { 162 content.document 163 .getElementById("vegetables") 164 .setAttribute("aria-expanded", "false"); 165 }); 166 await evt; 167 is( 168 outRows[2].getAttributeValue("AXDisclosing"), 169 0, 170 "Row is not disclosing after being collapsed again" 171 ); 172 } 173 ); 174 175 /** 176 * Test outline, outline rows with declared properties 177 */ 178 addAccessibleTask( 179 ` 180 <h3 id="tree1"> 181 Foods 182 </h3> 183 <ul role="tree" aria-labelledby="tree1" id="outline"> 184 <li role="treeitem" 185 aria-level="1" 186 aria-setsize="2" 187 aria-posinset="1" 188 aria-expanded="false"> 189 <span> 190 Fruits 191 </span> 192 <ul> 193 <li role="treeitem" 194 aria-level="3" 195 aria-setsize="2" 196 aria-posinset="1"> 197 Oranges 198 </li> 199 <li role="treeitem" 200 aria-level="2" 201 aria-setsize="2" 202 aria-posinset="2" 203 aria-expanded="true"> 204 <span> 205 Apples 206 </span> 207 <ul role="group"> 208 <li role="treeitem" 209 aria-level="3" 210 aria-setsize="2" 211 aria-posinset="1"> 212 Honeycrisp 213 </li> 214 <li role="treeitem" 215 aria-level="3" 216 aria-setsize="2" 217 aria-posinset="2"> 218 Granny Smith 219 </li> 220 </ul> 221 </li> 222 </ul> 223 </li> 224 <li role="treeitem" 225 aria-level="1" 226 aria-setsize="2" 227 aria-posinset="2" 228 aria-expanded="false"> 229 <span> 230 Vegetables 231 </span> 232 <ul role="group"> 233 <li role="treeitem" 234 aria-level="2" 235 aria-setsize="1" 236 aria-posinset="1" 237 aria-expanded="true"> 238 <span> 239 Podded Vegetables 240 </span> 241 <ul role="group"> 242 <li role="treeitem" 243 aria-level="3" 244 aria-setsize="2" 245 aria-posinset="1"> 246 Lentil 247 </li> 248 <li role="treeitem" 249 aria-level="3" 250 aria-setsize="2" 251 aria-posinset="2"> 252 Pea 253 </li> 254 </ul> 255 </li> 256 </ul> 257 </li> 258 </ul> 259 `, 260 async (browser, accDoc) => { 261 const outline = getNativeInterface(accDoc, "outline"); 262 is( 263 outline.getAttributeValue("AXRole"), 264 "AXOutline", 265 "Correct role for outline" 266 ); 267 268 const outChildren = outline.getAttributeValue("AXChildren"); 269 is(outChildren.length, 2, "Outline has two direct children"); 270 is(outChildren[0].getAttributeValue("AXSubrole"), "AXOutlineRow"); 271 is(outChildren[1].getAttributeValue("AXSubrole"), "AXOutlineRow"); 272 273 const outRows = outline.getAttributeValue("AXRows"); 274 is(outRows.length, 9, "Outline has nine rows"); 275 is( 276 outRows[0].getAttributeValue("AXDisclosing"), 277 0, 278 "Row is not disclosing" 279 ); 280 is( 281 outRows[0].getAttributeValue("AXDisclosedByRow"), 282 null, 283 "Row is direct child of outline" 284 ); 285 is( 286 outRows[0].getAttributeValue("AXDisclosedRows").length, 287 0, 288 "Row has no direct row children, has list" 289 ); 290 is( 291 outRows[0].getAttributeValue("AXDisclosureLevel"), 292 0, 293 "Row is level zero" 294 ); 295 296 is(outRows[2].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); 297 is( 298 outRows[2].getAttributeValue("AXDisclosedByRow"), 299 null, 300 "Row is direct child of group" 301 ); 302 is( 303 outRows[2].getAttributeValue("AXDisclosedRows").length, 304 2, 305 "Row has two row children" 306 ); 307 is( 308 outRows[2].getAttributeValue("AXDisclosureLevel"), 309 1, 310 "Row is level one" 311 ); 312 313 is( 314 outRows[3].getAttributeValue("AXDisclosing"), 315 0, 316 "Row is not disclosing" 317 ); 318 is( 319 outRows[3] 320 .getAttributeValue("AXDisclosedByRow") 321 .getAttributeValue("AXDescription"), 322 outRows[2].getAttributeValue("AXDescription"), 323 "Row is direct child of row 2" 324 ); 325 326 is( 327 outRows[3].getAttributeValue("AXDisclosedRows").length, 328 0, 329 "Row has no row children" 330 ); 331 is( 332 outRows[3].getAttributeValue("AXDisclosureLevel"), 333 2, 334 "Row is level two" 335 ); 336 337 is( 338 outRows[5].getAttributeValue("AXDisclosing"), 339 0, 340 "Row is not disclosing" 341 ); 342 is( 343 outRows[5].getAttributeValue("AXDisclosedByRow"), 344 null, 345 "Row is direct child of outline" 346 ); 347 is( 348 outRows[5].getAttributeValue("AXDisclosedRows").length, 349 1, 350 "Row has no one row child" 351 ); 352 is( 353 outRows[5].getAttributeValue("AXDisclosureLevel"), 354 0, 355 "Row is level zero" 356 ); 357 358 is(outRows[6].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); 359 is( 360 outRows[6] 361 .getAttributeValue("AXDisclosedByRow") 362 .getAttributeValue("AXDescription"), 363 outRows[5].getAttributeValue("AXDescription"), 364 "Row is direct child of row 5" 365 ); 366 is( 367 outRows[6].getAttributeValue("AXDisclosedRows").length, 368 2, 369 "Row has two row children" 370 ); 371 is( 372 outRows[6].getAttributeValue("AXDisclosureLevel"), 373 1, 374 "Row is level one" 375 ); 376 377 is( 378 outRows[7].getAttributeValue("AXDisclosing"), 379 0, 380 "Row is not disclosing" 381 ); 382 is( 383 outRows[7] 384 .getAttributeValue("AXDisclosedByRow") 385 .getAttributeValue("AXDescription"), 386 outRows[6].getAttributeValue("AXDescription"), 387 "Row is direct child of row 6" 388 ); 389 is( 390 outRows[7].getAttributeValue("AXDisclosedRows").length, 391 0, 392 "Row has no row children" 393 ); 394 is( 395 outRows[7].getAttributeValue("AXDisclosureLevel"), 396 2, 397 "Row is level two" 398 ); 399 } 400 ); 401 402 // Test outline that isn't built with li/uls gets correct desc 403 addAccessibleTask( 404 ` 405 <div role="tree" id="tree" tabindex="0" aria-label="My drive" aria-activedescendant="myfiles"> 406 <div id="myfiles" role="treeitem" aria-label="My files" aria-selected="true" aria-expanded="false">My files</div> 407 <div role="treeitem" aria-label="Shared items" aria-selected="false" aria-expanded="false">Shared items</div> 408 </div> 409 `, 410 async (browser, accDoc) => { 411 const tree = getNativeInterface(accDoc, "tree"); 412 is(tree.getAttributeValue("AXRole"), "AXOutline", "Correct role for tree"); 413 414 const treeItems = tree.getAttributeValue("AXChildren"); 415 is(treeItems.length, 2, "Outline has two direct children"); 416 is(treeItems[0].getAttributeValue("AXSubrole"), "AXOutlineRow"); 417 is(treeItems[1].getAttributeValue("AXSubrole"), "AXOutlineRow"); 418 419 const outRows = tree.getAttributeValue("AXRows"); 420 is(outRows.length, 2, "Outline has two rows"); 421 422 is( 423 outRows[0].getAttributeValue("AXDescription"), 424 "My files", 425 "files labelled correctly" 426 ); 427 is( 428 outRows[1].getAttributeValue("AXDescription"), 429 "Shared items", 430 "shared items labelled correctly" 431 ); 432 } 433 ); 434 435 // Test outline registers AXDisclosed attr as settable 436 addAccessibleTask( 437 ` 438 <div role="tree" id="tree" tabindex="0" aria-label="My drive" aria-activedescendant="myfiles"> 439 <div id="myfiles" role="treeitem" aria-label="My files" aria-selected="true" aria-expanded="false">My files</div> 440 <div role="treeitem" aria-label="Shared items" aria-selected="false" aria-expanded="true">Shared items</div> 441 </div> 442 `, 443 async (browser, accDoc) => { 444 const tree = getNativeInterface(accDoc, "tree"); 445 const treeItems = tree.getAttributeValue("AXChildren"); 446 447 is(treeItems.length, 2, "Outline has two direct children"); 448 is(treeItems[0].getAttributeValue("AXDisclosing"), 0); 449 is(treeItems[1].getAttributeValue("AXDisclosing"), 1); 450 451 is(treeItems[0].isAttributeSettable("AXDisclosing"), true); 452 is(treeItems[1].isAttributeSettable("AXDisclosing"), true); 453 454 // attempt to change attribute values 455 treeItems[0].setAttributeValue("AXDisclosing", 1); 456 treeItems[0].setAttributeValue("AXDisclosing", 0); 457 458 // verify they're unchanged 459 is(treeItems[0].getAttributeValue("AXDisclosing"), 0); 460 is(treeItems[1].getAttributeValue("AXDisclosing"), 1); 461 } 462 ); 463 464 // Test outline rows correctly expose checkable, checked/unchecked/mixed status 465 addAccessibleTask( 466 ` 467 <div role="tree" id="tree"> 468 <div role="treeitem" aria-checked="false" id="l1"> 469 Leaf 1 470 </div> 471 <div role="treeitem" aria-checked="true" id="l2"> 472 Leaf 2 473 </div> 474 <div role="treeitem" id="l3"> 475 Leaf 3 476 </div> 477 <div role="treeitem" aria-checked="mixed" id="l4"> 478 Leaf 4 479 </div> 480 </div> 481 482 `, 483 async (browser, accDoc) => { 484 const tree = getNativeInterface(accDoc, "tree"); 485 const treeItems = tree.getAttributeValue("AXChildren"); 486 487 is(treeItems.length, 4, "Outline has four direct children"); 488 is( 489 treeItems[0].getAttributeValue("AXValue"), 490 0, 491 "Child one is not checked" 492 ); 493 is(treeItems[1].getAttributeValue("AXValue"), 1, "Child two is checked"); 494 is( 495 treeItems[2].getAttributeValue("AXValue"), 496 null, 497 "Child three is not checkable and has no val" 498 ); 499 is(treeItems[3].getAttributeValue("AXValue"), 2, "Child four is mixed"); 500 501 let stateChanged = Promise.all([ 502 waitForMacEvent("AXValueChanged", "l1"), 503 waitForStateChange("l1", STATE_CHECKED, true), 504 ]); 505 // We should get a state change event for checked. 506 await SpecialPowers.spawn(browser, [], () => { 507 content.document 508 .getElementById("l1") 509 .setAttribute("aria-checked", "true"); 510 }); 511 await stateChanged; 512 is(treeItems[0].getAttributeValue("AXValue"), 1, "Child one is checked"); 513 514 stateChanged = Promise.all([ 515 waitForMacEvent("AXValueChanged", "l2"), 516 waitForMacEvent("AXValueChanged", "l2"), 517 waitForStateChange("l2", STATE_CHECKED, false), 518 waitForStateChange("l2", STATE_CHECKABLE, false), 519 ]); 520 // We should get a state change event for both checked and checkable, 521 // and value changes for both. 522 await SpecialPowers.spawn(browser, [], () => { 523 content.document.getElementById("l2").removeAttribute("aria-checked"); 524 }); 525 await stateChanged; 526 is( 527 treeItems[1].getAttributeValue("AXValue"), 528 null, 529 "Child two is not checkable and has no val" 530 ); 531 532 stateChanged = Promise.all([ 533 waitForMacEvent("AXValueChanged", "l3"), 534 waitForMacEvent("AXValueChanged", "l3"), 535 waitForStateChange("l3", STATE_CHECKED, true), 536 waitForStateChange("l3", STATE_CHECKABLE, true), 537 ]); 538 // We should get a state change event for both checked and checkable, 539 // and value changes for each. 540 await SpecialPowers.spawn(browser, [], () => { 541 content.document 542 .getElementById("l3") 543 .setAttribute("aria-checked", "true"); 544 }); 545 await stateChanged; 546 is(treeItems[2].getAttributeValue("AXValue"), 1, "Child three is checked"); 547 548 stateChanged = Promise.all([ 549 waitForMacEvent("AXValueChanged", "l4"), 550 waitForMacEvent("AXValueChanged", "l4"), 551 waitForStateChange("l4", STATE_MIXED, false), 552 waitForStateChange("l4", STATE_CHECKABLE, false), 553 ]); 554 // We should get a state change event for both mixed and checkable, 555 // and value changes for each. 556 await SpecialPowers.spawn(browser, [], () => { 557 content.document.getElementById("l4").removeAttribute("aria-checked"); 558 }); 559 await stateChanged; 560 is( 561 treeItems[3].getAttributeValue("AXValue"), 562 null, 563 "Child four is not checkable and has no value" 564 ); 565 } 566 );