browser_accessibility_keyboard_audit.js (11326B)
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 /** 8 * Checks functionality around keyboard audit for the AccessibleActor. 9 */ 10 11 const { 12 accessibility: { 13 AUDIT_TYPE: { KEYBOARD }, 14 SCORES: { FAIL, WARNING }, 15 ISSUE_TYPE: { 16 [KEYBOARD]: { 17 FOCUSABLE_NO_SEMANTICS, 18 FOCUSABLE_POSITIVE_TABINDEX, 19 INTERACTIVE_NOT_FOCUSABLE, 20 MOUSE_INTERACTIVE_ONLY, 21 NO_FOCUS_VISIBLE, 22 }, 23 }, 24 }, 25 } = require("resource://devtools/shared/constants.js"); 26 27 add_task(async function () { 28 const { target, walker, parentAccessibility, a11yWalker } = 29 await initAccessibilityFrontsForUrl( 30 `${MAIN_DOMAIN}doc_accessibility_keyboard_audit.html` 31 ); 32 33 const tests = [ 34 [ 35 "Focusable element (styled button) with no semantics.", 36 "#button-1", 37 { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS }, 38 ], 39 ["Element (styled button) with no semantics.", "#button-2", null], 40 [ 41 "Container element for out of order focusable element.", 42 "#input-container", 43 null, 44 ], 45 [ 46 "Interactive element with focus out of order (-1).", 47 "#input-1", 48 { 49 score: FAIL, 50 issue: INTERACTIVE_NOT_FOCUSABLE, 51 }, 52 ], 53 [ 54 "Interactive element with focus out of order (-1) when disabled.", 55 "#input-2", 56 null, 57 ], 58 ["Interactive element when disabled.", "#input-3", null], 59 ["Focusable interactive element.", "#input-4", null], 60 [ 61 "Interactive accessible (link with no attributes) with no accessible actions.", 62 "#link-1", 63 null, 64 ], 65 ["Interactive accessible (link with valid href).", "#link-2", null], 66 ["Interactive accessible (link with # as href).", "#link-3", null], 67 [ 68 "Interactive accessible (link with empty string as href).", 69 "#link-4", 70 null, 71 ], 72 ["Interactive accessible with no tabindex.", "#button-3", null], 73 [ 74 "Interactive accessible with -1 tabindex.", 75 "#button-4", 76 { 77 score: FAIL, 78 issue: INTERACTIVE_NOT_FOCUSABLE, 79 }, 80 ], 81 ["Interactive accessible with 0 tabindex.", "#button-5", null], 82 [ 83 "Interactive accessible with 1 tabindex.", 84 "#button-6", 85 { score: WARNING, issue: FOCUSABLE_POSITIVE_TABINDEX }, 86 ], 87 [ 88 "Focusable ARIA button with no focus styling.", 89 "#focusable-1", 90 { score: WARNING, issue: NO_FOCUS_VISIBLE }, 91 ], 92 ["Focusable ARIA button with focus styling.", "#focusable-2", null], 93 ["Focusable ARIA button with browser styling.", "#focusable-3", null], 94 [ 95 "Not focusable, non-semantic element that has a click handler.", 96 "#mouse-only-1", 97 { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY }, 98 ], 99 [ 100 "Focusable, non-semantic element that has a click handler.", 101 "#focusable-4", 102 { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS }, 103 ], 104 [ 105 "Not focusable, ARIA button that has a click handler.", 106 "#button-7", 107 { score: FAIL, issue: INTERACTIVE_NOT_FOCUSABLE }, 108 ], 109 ["Focusable, ARIA button with a click handler.", "#button-8", null], 110 ["Regular image, no keyboard checks should flag an issue.", "#img-1", null], 111 [ 112 "Image with a longdesc (accessible will have showlongdesc action).", 113 "#img-2", 114 null, 115 ], 116 [ 117 "Clickable image with a longdesc (accessible will have click and showlongdesc actions).", 118 "#img-3", 119 { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY }, 120 ], 121 [ 122 "Clickable image (accessible will have click action).", 123 "#img-4", 124 { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY }, 125 ], 126 ["Focusable button with aria-haspopup.", "#buttonmenu-1", null], 127 [ 128 "Not focusable aria button with aria-haspopup.", 129 "#buttonmenu-2", 130 { 131 score: FAIL, 132 issue: INTERACTIVE_NOT_FOCUSABLE, 133 }, 134 ], 135 ["Focusable checkbox.", "#checkbox-1", null], 136 ["Focusable select element size > 1", "#listbox-1", null], 137 ["Focusable select element with one option", "#combobox-1", null], 138 ["Focusable select element with no options", "#combobox-2", null], 139 ["Focusable select element with two options", "#combobox-3", null], 140 [ 141 "Non-focusable aria combobox with one aria option.", 142 "#editcombobox-1", 143 null, 144 ], 145 ["Non-focusable aria combobox with no options.", "#editcombobox-2", null], 146 ["Focusable aria combobox with no options.", "#editcombobox-3", null], 147 [ 148 "Non-focusable aria switch", 149 "#switch-1", 150 { 151 score: FAIL, 152 issue: INTERACTIVE_NOT_FOCUSABLE, 153 }, 154 ], 155 ["Focusable aria switch", "#switch-2", null], 156 [ 157 "Combobox list that is visible (has focusable state)", 158 "#owned_listbox", 159 null, 160 ], 161 [ 162 "Mouse interactive, label that contains form element (linked)", 163 "#label-1", 164 null, 165 ], 166 ["Mouse interactive label for external element (linked)", "#label-2", null], 167 ["Not interactive unlinked label", "#label-3", null], 168 [ 169 "Not interactive unlinked label with folloing form element", 170 "#label-4", 171 null, 172 ], 173 ["Image inside an anchor (href)", "#img-5", null], 174 ["Image inside an anchor (onmousedown)", "#img-6", null], 175 ["Image inside an anchor (onclick)", "#img-7", null], 176 ["Image inside an anchor (onmouseup)", "#img-8", null], 177 [ 178 "Section with a collapse action from aria-expanded attribute", 179 "#section-1", 180 null, 181 ], 182 ["Tabindex -1 should not report an element as focusable", "#main", null], 183 [ 184 "Not keyboard focusable element with no focus styling.", 185 "#not-keyboard-focusable-1", 186 null, 187 ], 188 ["Interactive grid that is not focusable.", "#grid-1", null], 189 [ 190 "Focusable interactive grid.", 191 "#grid-2", 192 { score: "WARNING", issue: "FOCUSABLE_NO_SEMANTICS" }, 193 ], 194 [ 195 "Non interactive ARIA table does not need to be focusable.", 196 "#table-1", 197 null, 198 ], 199 [ 200 "Focusable ARIA table does not have interactive semantics", 201 "#table-2", 202 { score: "WARNING", issue: "FOCUSABLE_NO_SEMANTICS" }, 203 ], 204 ["Non interactive table does not need to be focusable.", "#table-3", null], 205 [ 206 "Focusable table does not have interactive semantics", 207 "#table-4", 208 { score: "WARNING", issue: "FOCUSABLE_NO_SEMANTICS" }, 209 ], 210 [ 211 "Article that is not focusable is not considered interactive", 212 "#article-1", 213 null, 214 ], 215 ["Focusable article is considered interactive", "#article-2", null], 216 [ 217 "Column header that is not focusable is not considered interactive (ARIA grid)", 218 "#columnheader-1", 219 null, 220 ], 221 [ 222 "Column header that is not focusable is not considered interactive (ARIA table)", 223 "#columnheader-2", 224 null, 225 ], 226 [ 227 "Column header that is not focusable is not considered interactive (table)", 228 "#columnheader-3", 229 null, 230 ], 231 [ 232 "Column header that is focusable is considered interactive (table)", 233 "#columnheader-4", 234 null, 235 ], 236 [ 237 "Column header that is not focusable is not considered interactive (table as ARIA grid)", 238 "#columnheader-5", 239 null, 240 ], 241 [ 242 "Column header that is focusable is considered interactive (table as ARIA grid)", 243 "#columnheader-6", 244 null, 245 ], 246 [ 247 "Row header that is not focusable is not considered interactive", 248 "#rowheader-1", 249 null, 250 ], 251 [ 252 "Row header that is not focusable is not considered interactive", 253 "#rowheader-2", 254 null, 255 ], 256 [ 257 "Row header that is not focusable is not considered interactive", 258 "#rowheader-3", 259 null, 260 ], 261 [ 262 "Row header that is focusable is considered interactive", 263 "#rowheader-4", 264 null, 265 ], 266 [ 267 "Row header that is not focusable is not considered interactive (table as ARIA grid)", 268 "#rowheader-5", 269 null, 270 ], 271 [ 272 "Row header that is focusable is considered interactive (table as ARIA grid)", 273 "#rowheader-6", 274 null, 275 ], 276 [ 277 "Gridcell that is not focusable is not considered interactive (ARIA grid)", 278 "#gridcell-1", 279 null, 280 ], 281 [ 282 "Gridcell that is focusable is considered interactive (ARIA grid)", 283 "#gridcell-2", 284 null, 285 ], 286 [ 287 "Gridcell that is not focusable is not considered interactive (table as ARIA grid)", 288 "#gridcell-3", 289 null, 290 ], 291 [ 292 "Gridcell that is focusable is considered interactive (table as ARIA grid)", 293 "#gridcell-4", 294 null, 295 ], 296 [ 297 "Tab list that is not focusable is not considered interactive", 298 "#tablist-1", 299 null, 300 ], 301 ["Focusable tab list is considered interactive", "#tablist-2", null], 302 [ 303 "Scrollbar that is not focusable is not considered interactive", 304 "#scrollbar-1", 305 null, 306 ], 307 ["Focusable scrollbar is considered interactive", "#scrollbar-2", null], 308 [ 309 "Separator that is not focusable is not considered interactive", 310 "#separator-1", 311 null, 312 ], 313 ["Focusable separator is considered interactive", "#separator-2", null], 314 [ 315 "Toolbar that is not focusable is not considered interactive", 316 "#toolbar-1", 317 null, 318 ], 319 ["Focusable toolbar is considered interactive", "#toolbar-2", null], 320 [ 321 "Menu popup that is not focusable is not considered interactive", 322 "#menu-1", 323 null, 324 ], 325 ["Focusable menu popup is considered interactive", "#menu-2", null], 326 [ 327 "Menubar that is not focusable is not considered interactive", 328 "#menubar-1", 329 null, 330 ], 331 ["Focusable menubar is considered interactive", "#menubar-2", null], 332 ["input type=search is considered interactive", "#input-search", null], 333 ]; 334 335 for (const [description, selector, expected] of tests) { 336 info(description); 337 const node = await walker.querySelector(walker.rootNode, selector); 338 const front = await a11yWalker.getAccessibleFor(node); 339 const audit = await front.audit({ types: [KEYBOARD] }); 340 Assert.deepEqual( 341 audit[KEYBOARD], 342 expected, 343 `Audit result for ${selector} is correct.` 344 ); 345 } 346 347 info("Text leaf inside a link (jump action is propagated to the text link)"); 348 let node = await walker.querySelector(walker.rootNode, "#link-5"); 349 let parent = await a11yWalker.getAccessibleFor(node); 350 let front = (await parent.children())[0]; 351 let audit = await front.audit({ types: [KEYBOARD] }); 352 Assert.deepEqual( 353 audit[KEYBOARD], 354 null, 355 "Text leafs are excluded from semantics rule." 356 ); 357 358 info("Combobox list that is invisible"); 359 node = await walker.querySelector(walker.rootNode, "#combobox-1"); 360 parent = await a11yWalker.getAccessibleFor(node); 361 front = (await parent.children())[0]; 362 audit = await front.audit({ types: [KEYBOARD] }); 363 Assert.deepEqual( 364 audit[KEYBOARD], 365 null, 366 "Combobox lists (invisible) are excluded from semantics rule." 367 ); 368 369 await waitForA11yShutdown(parentAccessibility); 370 await target.destroy(); 371 gBrowser.removeCurrentTab(); 372 });