browser_generalProps.js (15366B)
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 /* eslint-disable camelcase */ 15 // From https://learn.microsoft.com/en-us/windows/win32/winauto/landmark-type-identifiers 16 const UIA_CustomLandmarkTypeId = 80000; 17 const UIA_MainLandmarkTypeId = 80002; 18 /* eslint-enable camelcase */ 19 20 /** 21 * Test the Name property. 22 */ 23 addUiaTask( 24 ` 25 <button id="button">before</button> 26 <div id="div">div</div> 27 `, 28 async function testName(browser) { 29 await definePyVar("doc", `getDocUia()`); 30 await assignPyVarToUiaWithId("button"); 31 is( 32 await runPython(`button.CurrentName`), 33 "before", 34 "button has correct name" 35 ); 36 await assignPyVarToUiaWithId("div"); 37 is(await runPython(`div.CurrentName`), "", "div has no name"); 38 39 info("Setting aria-label on button"); 40 await setUpWaitForUiaPropEvent("Name", "button"); 41 await invokeSetAttribute(browser, "button", "aria-label", "after"); 42 await waitForUiaEvent(); 43 ok(true, "Got Name prop change event on button"); 44 is( 45 await runPython(`button.CurrentName`), 46 "after", 47 "button has correct name" 48 ); 49 } 50 ); 51 52 /** 53 * Test the FullDescription property. 54 */ 55 addUiaTask( 56 ` 57 <button id="button" aria-description="before">button</button> 58 <div id="div">div</div> 59 `, 60 async function testFullDescription(browser) { 61 await definePyVar("doc", `getDocUia()`); 62 await assignPyVarToUiaWithId("button"); 63 is( 64 await runPython(`button.CurrentFullDescription`), 65 "before", 66 "button has correct FullDescription" 67 ); 68 await assignPyVarToUiaWithId("div"); 69 is( 70 await runPython(`div.CurrentFullDescription`), 71 "", 72 "div has no FullDescription" 73 ); 74 75 info("Setting aria-description on button"); 76 await setUpWaitForUiaPropEvent("FullDescription", "button"); 77 await invokeSetAttribute(browser, "button", "aria-description", "after"); 78 await waitForUiaEvent(); 79 ok(true, "Got FullDescription prop change event on button"); 80 is( 81 await runPython(`button.CurrentFullDescription`), 82 "after", 83 "button has correct FullDescription" 84 ); 85 }, 86 // The IA2 -> UIA proxy doesn't support FullDescription. 87 { uiaEnabled: true, uiaDisabled: false } 88 ); 89 90 /** 91 * Test the IsEnabled property. 92 */ 93 addUiaTask( 94 ` 95 <button id="button">button</button> 96 <p id="p">p</p> 97 `, 98 async function testIsEnabled(browser) { 99 await definePyVar("doc", `getDocUia()`); 100 await assignPyVarToUiaWithId("button"); 101 ok(await runPython(`button.CurrentIsEnabled`), "button has IsEnabled true"); 102 // The IA2 -> UIA proxy doesn't fire IsEnabled prop change events. 103 if (gIsUiaEnabled) { 104 info("Setting disabled on button"); 105 await setUpWaitForUiaPropEvent("IsEnabled", "button"); 106 await invokeSetAttribute(browser, "button", "disabled", true); 107 await waitForUiaEvent(); 108 ok(true, "Got IsEnabled prop change event on button"); 109 ok( 110 !(await runPython(`button.CurrentIsEnabled`)), 111 "button has IsEnabled false" 112 ); 113 } 114 115 await assignPyVarToUiaWithId("p"); 116 ok(await runPython(`p.CurrentIsEnabled`), "p has IsEnabled true"); 117 } 118 ); 119 120 async function testGroupPos(id, level, pos, size) { 121 await assignPyVarToUiaWithId(id); 122 is(await runPython(`${id}.CurrentLevel`), level, `${id} Level correct`); 123 is( 124 await runPython(`${id}.CurrentPositionInSet`), 125 pos, 126 `${id} PositionInSet correct` 127 ); 128 is( 129 await runPython(`${id}.CurrentSizeOfSet`), 130 size, 131 `${id} SizeOfSet correct` 132 ); 133 } 134 135 /** 136 * Test the Level, PositionInSet and SizeOfSet properties. 137 */ 138 addUiaTask( 139 ` 140 <ul> 141 <li id="li1">li1<ul id="ul1"> 142 <li id="li2a">li2a</li> 143 <li id="li2b" hidden>li2b</li> 144 <li id="li2c">li2c</li> 145 </ul></li> 146 </ul> 147 <h2 id="h2">h2</h2> 148 <button id="button">button</button> 149 `, 150 async function testGroupPosProps(browser) { 151 await definePyVar("doc", `getDocUia()`); 152 await testGroupPos("li1", 1, 1, 1); 153 await testGroupPos("li2a", 2, 1, 2); 154 await testGroupPos("li2c", 2, 2, 2); 155 info("Showing li2b"); 156 // There aren't events in any API for a change to group position properties 157 // because this would be too spammy and isn't particularly useful given 158 // how frequently these can change. 159 let shown = waitForEvent(EVENT_SHOW, "li2b"); 160 await invokeContentTask(browser, [], () => { 161 content.document.getElementById("li2b").hidden = false; 162 }); 163 await shown; 164 await testGroupPos("li2a", 2, 1, 3); 165 await testGroupPos("li2b", 2, 2, 3); 166 await testGroupPos("li2c", 2, 3, 3); 167 168 // The IA2 -> UIA proxy doesn't map heading level to the Level property. 169 if (gIsUiaEnabled) { 170 await testGroupPos("h2", 2, 0, 0); 171 } 172 await testGroupPos("button", 0, 0, 0); 173 } 174 ); 175 176 /** 177 * Test the FrameworkId property. 178 */ 179 addUiaTask( 180 `<button id="button">button</button>`, 181 async function testFrameworkId() { 182 await definePyVar("doc", `getDocUia()`); 183 is( 184 await runPython(`doc.CurrentFrameworkId`), 185 "Gecko", 186 "doc FrameworkId is correct" 187 ); 188 await assignPyVarToUiaWithId("button"); 189 is( 190 await runPython(`button.CurrentFrameworkId`), 191 "Gecko", 192 "button FrameworkId is correct" 193 ); 194 } 195 ); 196 197 /** 198 * Test the ClassName property. 199 */ 200 addUiaTask( 201 ` 202 <p id="p">p</p> 203 <button id="button" class="c1">button</button> 204 `, 205 async function testClassName(browser, docAcc) { 206 await definePyVar("doc", `getDocUia()`); 207 await assignPyVarToUiaWithId("p"); 208 ok(!(await runPython(`p.CurrentClassName`)), "p has no ClassName"); 209 210 await assignPyVarToUiaWithId("button"); 211 is( 212 await runPython(`button.CurrentClassName`), 213 "c1", 214 "button has correct ClassName" 215 ); 216 info("Changing button class"); 217 await invokeSetAttribute(browser, "button", "class", "c2 c3"); 218 // Gecko doesn't fire an event for class changes, as this isn't useful for 219 // clients. 220 const button = findAccessibleChildByID(docAcc, "button"); 221 await untilCacheIs( 222 () => button.attributes.getStringProperty("class"), 223 "c2 c3", 224 "button class updated" 225 ); 226 is( 227 await runPython(`button.CurrentClassName`), 228 "c2 c3", 229 "button has correct ClassName" 230 ); 231 }, 232 // The IA2 -> UIA proxy doesn't support ClassName. 233 { uiaEnabled: true, uiaDisabled: false } 234 ); 235 236 /** 237 * Test the AriaRole property. 238 */ 239 addUiaTask( 240 ` 241 <div id="button" role="button">button</div> 242 <div id="main" role="main">main</div> 243 <div id="unknown" role="unknown">unknown</div> 244 <button id="computedButton">computedButton</button> 245 <h1 id="computedHeading">computedHeading</h1> 246 <main id="computedMain">computedMain</main> 247 <div id="generic">generic</div> 248 `, 249 async function testAriaRole() { 250 await definePyVar("doc", `getDocUia()`); 251 is( 252 await runPython(`findUiaByDomId(doc, "button").CurrentAriaRole`), 253 "button", 254 "button has correct AriaRole" 255 ); 256 is( 257 await runPython(`findUiaByDomId(doc, "main").CurrentAriaRole`), 258 "main", 259 "main has correct AriaRole" 260 ); 261 is( 262 await runPython(`findUiaByDomId(doc, "unknown").CurrentAriaRole`), 263 "unknown", 264 "unknown has correct AriaRole" 265 ); 266 // The IA2 -> UIA proxy doesn't compute ARIA roles. 267 if (gIsUiaEnabled) { 268 is( 269 await runPython( 270 `findUiaByDomId(doc, "computedButton").CurrentAriaRole` 271 ), 272 "button", 273 "computedButton has correct AriaRole" 274 ); 275 is( 276 await runPython(`findUiaByDomId(doc, "computedMain").CurrentAriaRole`), 277 "main", 278 "computedMain has correct AriaRole" 279 ); 280 is( 281 await runPython( 282 `findUiaByDomId(doc, "computedHeading").CurrentAriaRole` 283 ), 284 "heading", 285 "computedHeading has correct AriaRole" 286 ); 287 is( 288 await runPython(`findUiaByDomId(doc, "generic").CurrentAriaRole`), 289 "generic", 290 "generic has correct AriaRole" 291 ); 292 } 293 } 294 ); 295 296 /** 297 * Test the LocalizedControlType property. We don't support this ourselves, but 298 * the system provides it based on ControlType and AriaRole. 299 */ 300 addUiaTask( 301 ` 302 <button id="button">button</button> 303 <h1 id="h1">h1</h1> 304 <main id="main">main</main> 305 `, 306 async function testLocalizedControlType() { 307 await definePyVar("doc", `getDocUia()`); 308 is( 309 await runPython( 310 `findUiaByDomId(doc, "button").CurrentLocalizedControlType` 311 ), 312 "button", 313 "button has correct LocalizedControlType" 314 ); 315 // The IA2 -> UIA proxy doesn't compute ARIA roles, so it can't compute the 316 // correct LocalizedControlType for these either. 317 if (gIsUiaEnabled) { 318 is( 319 await runPython( 320 `findUiaByDomId(doc, "h1").CurrentLocalizedControlType` 321 ), 322 "heading", 323 "h1 has correct LocalizedControlType" 324 ); 325 is( 326 await runPython( 327 `findUiaByDomId(doc, "main").CurrentLocalizedControlType` 328 ), 329 "main", 330 "main has correct LocalizedControlType" 331 ); 332 } 333 } 334 ); 335 336 /** 337 * Test the LandmarkType property. 338 */ 339 addUiaTask( 340 ` 341 <div id="main" role="main">main</div> 342 <main id="htmlMain">htmlMain</main> 343 <div id="banner" role="banner">banner</div> 344 <header id="header">header</header> 345 <div id="region" role="region" aria-label="region">region</div> 346 <div id="unnamedRegion" role="region">unnamedRegion</div> 347 <main id="mainBanner" role="banner">mainBanner</main> 348 <div id="none">none</div> 349 `, 350 async function testLandmarkType() { 351 await definePyVar("doc", `getDocUia()`); 352 is( 353 await runPython(`findUiaByDomId(doc, "main").CurrentLandmarkType`), 354 UIA_MainLandmarkTypeId, 355 "main has correct LandmarkType" 356 ); 357 is( 358 await runPython(`findUiaByDomId(doc, "htmlMain").CurrentLandmarkType`), 359 UIA_MainLandmarkTypeId, 360 "htmlMain has correct LandmarkType" 361 ); 362 is( 363 await runPython(`findUiaByDomId(doc, "banner").CurrentLandmarkType`), 364 UIA_CustomLandmarkTypeId, 365 "banner has correct LandmarkType" 366 ); 367 is( 368 await runPython(`findUiaByDomId(doc, "header").CurrentLandmarkType`), 369 UIA_CustomLandmarkTypeId, 370 "header has correct LandmarkType" 371 ); 372 is( 373 await runPython(`findUiaByDomId(doc, "region").CurrentLandmarkType`), 374 UIA_CustomLandmarkTypeId, 375 "region has correct LandmarkType" 376 ); 377 is( 378 await runPython( 379 `findUiaByDomId(doc, "unnamedRegion").CurrentLandmarkType` 380 ), 381 0, 382 "unnamedRegion has correct LandmarkType" 383 ); 384 // ARIA role takes precedence. 385 is( 386 await runPython(`findUiaByDomId(doc, "mainBanner").CurrentLandmarkType`), 387 UIA_CustomLandmarkTypeId, 388 "mainBanner has correct LandmarkType" 389 ); 390 is( 391 await runPython(`findUiaByDomId(doc, "none").CurrentLandmarkType`), 392 0, 393 "none has correct LandmarkType" 394 ); 395 } 396 ); 397 398 /** 399 * Test the LocalizedLandmarkType property. 400 */ 401 addUiaTask( 402 ` 403 <div id="main" role="main">main</div> 404 <div id="contentinfo" role="contentinfo">contentinfo</div> 405 <div id="region" role="region" aria-label="region">region</div> 406 <div id="unnamedRegion" role="region">unnamedRegion</div> 407 <main id="mainBanner" role="banner">mainBanner</main> 408 <div id="none">none</div> 409 `, 410 async function testLocalizedLandmarkType() { 411 await definePyVar("doc", `getDocUia()`); 412 // Provided by the system. 413 is( 414 await runPython( 415 `findUiaByDomId(doc, "main").CurrentLocalizedLandmarkType` 416 ), 417 "main", 418 "main has correct LocalizedLandmarkType" 419 ); 420 // The IA2 -> UIA proxy doesn't follow the Core AAM spec for this role. 421 if (gIsUiaEnabled) { 422 // Provided by us. 423 is( 424 await runPython( 425 `findUiaByDomId(doc, "contentinfo").CurrentLocalizedLandmarkType` 426 ), 427 "content information", 428 "contentinfo has correct LocalizedLandmarkType" 429 ); 430 } 431 is( 432 await runPython( 433 `findUiaByDomId(doc, "region").CurrentLocalizedLandmarkType` 434 ), 435 "region", 436 "region has correct LocalizedLandmarkType" 437 ); 438 // Invalid landmark. 439 is( 440 await runPython( 441 `findUiaByDomId(doc, "unnamedRegion").CurrentLocalizedLandmarkType` 442 ), 443 "", 444 "unnamedRegion has correct LocalizedLandmarkType" 445 ); 446 // ARIA role takes precedence. 447 is( 448 await runPython( 449 `findUiaByDomId(doc, "mainBanner").CurrentLocalizedLandmarkType` 450 ), 451 "banner", 452 "mainBanner has correct LocalizedLandmarkType" 453 ); 454 is( 455 await runPython( 456 `findUiaByDomId(doc, "none").CurrentLocalizedLandmarkType` 457 ), 458 "", 459 "none has correct LocalizedLandmarkType" 460 ); 461 } 462 ); 463 464 /** 465 * Test the AcceleratorKey property. 466 */ 467 addUiaTask( 468 ` 469 <div id="button" role="button" aria-keyshortcuts="Alt+Shift+f">foo</div> 470 `, 471 async function testAcceleratorKey() { 472 await definePyVar("doc", `getDocUia()`); 473 is( 474 await runPython(`findUiaByDomId(doc, "button").CurrentAcceleratorKey`), 475 "Alt+Shift+f", 476 "button has correct AcceleratorKey" 477 ); 478 }, 479 { uiaEnabled: true, uiaDisabled: false } 480 ); 481 482 /** 483 * Test the IsOffscreen property. 484 */ 485 addUiaTask( 486 ` 487 <button id="onscreen">onscreen</button> 488 <button id="offscreen" style="position: absolute; left: -10000px;">offscreen</button> 489 `, 490 async function testIsOffscreen(browser, docAcc) { 491 await definePyVar("doc", `getDocUia()`); 492 ok( 493 !(await runPython(`findUiaByDomId(doc, "onscreen").CurrentIsOffscreen`)), 494 "onscreen has correct IsOffscreen" 495 ); 496 ok( 497 await runPython(`findUiaByDomId(doc, "offscreen").CurrentIsOffscreen`), 498 "offscreen has correct IsOffscreen" 499 ); 500 ok( 501 !(await runPython(`doc.CurrentIsOffscreen`)), 502 "doc has correct IsOffscreen" 503 ); 504 info("Opening a new tab"); 505 await BrowserTestUtils.withNewTab("", async () => { 506 // withNewTab (nor a focus event) isn't enough to guarantee the new tab is 507 // fully active in the foreground yet. 508 await untilCacheOk(() => { 509 const [state] = getStates(docAcc); 510 return state & STATE_OFFSCREEN; 511 }, "doc is offscreen in cross-platform tree"); 512 // doc is now a background tab, so it should be offscreen. 513 ok( 514 await runPython(`doc.CurrentIsOffscreen`), 515 "doc has correct IsOffscreen" 516 ); 517 }); 518 }, 519 { uiaEnabled: true, uiaDisabled: true } 520 ); 521 522 /** 523 * Test the IsPassword property. 524 */ 525 addUiaTask( 526 ` 527 <input type="text" id="text"> 528 <input type="password" id="password"> 529 `, 530 async function testIsPassword() { 531 await definePyVar("doc", `getDocUia()`); 532 ok( 533 !(await runPython(`findUiaByDomId(doc, "text").CurrentIsPassword`)), 534 "text has correct IsPassword" 535 ); 536 ok( 537 await runPython(`findUiaByDomId(doc, "password").CurrentIsPassword`), 538 "password has correct IsPassword" 539 ); 540 ok( 541 !(await runPython(`doc.CurrentIsPassword`)), 542 "doc has correct IsPassword" 543 ); 544 }, 545 { uiaEnabled: true, uiaDisabled: true } 546 );