browser_aria_errormessage.js (9764B)
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 * This test verifies that an input with aria-invalid="true/grammar/spelling" exposes the MOX accessible for 9 * its error message via AXErrorMessageElements. 10 */ 11 addAccessibleTask( 12 ` 13 <input id="input-invalid" aria-invalid="true" aria-errormessage="error-msg"> 14 <input id="input-invalid-grammar" aria-invalid="grammar" aria-errormessage="error-msg"> 15 <input id="input-invalid-spelling" aria-invalid="spelling" aria-errormessage="error-msg"> 16 <div id="error-msg">Field validation failed</div> 17 `, 18 (_browser, accDoc) => { 19 const messagesInvalid = getNativeInterface(accDoc, "input-invalid") 20 .getAttributeValue("AXErrorMessageElements") 21 .map(e => e.getAttributeValue("AXDOMIdentifier")); 22 23 is( 24 messagesInvalid.length, 25 1, 26 "Only one element referenced via 'aria-errormessage'" 27 ); 28 is( 29 messagesInvalid[0], 30 "error-msg", 31 "input#input-invalid refers 'error-msg' in the 'aria-errormessage'" 32 ); 33 34 const messagesInvalidGrammar = getNativeInterface( 35 accDoc, 36 "input-invalid-grammar" 37 ) 38 .getAttributeValue("AXErrorMessageElements") 39 .map(e => e.getAttributeValue("AXDOMIdentifier")); 40 41 is( 42 messagesInvalidGrammar.length, 43 1, 44 "Only one element referenced via 'aria-errormessage'" 45 ); 46 is( 47 messagesInvalidGrammar[0], 48 "error-msg", 49 "input#input-invalid-grammar refers 'error-msg' in the 'aria-errormessage'" 50 ); 51 52 const messagesInvalidSpelling = getNativeInterface( 53 accDoc, 54 "input-invalid-spelling" 55 ) 56 .getAttributeValue("AXErrorMessageElements") 57 .map(e => e.getAttributeValue("AXDOMIdentifier")); 58 59 is( 60 messagesInvalidSpelling.length, 61 1, 62 "Only one element referenced via 'aria-errormessage'" 63 ); 64 is( 65 messagesInvalidSpelling[0], 66 "error-msg", 67 "input#input-invalid-spelling refers 'error-msg' in the 'aria-errormessage'" 68 ); 69 } 70 ); 71 72 /** 73 * This test verifies that an input with aria-invalid=true exposes all the MOX accessibles defined through `aria-errormessage` 74 * via AXErrorMessageElements 75 */ 76 addAccessibleTask( 77 ` 78 <label for="input">Field with error</label><input id="input" aria-invalid="true" aria-errormessage="error-msg-specialchar error-msg-10charlong"> 79 <div id="error-msg-specialchar">Field must contain special characters</div> 80 <div id="error-msg-10charlong">Field must contain more than 10 characters</div> 81 `, 82 (_browser, accDoc) => { 83 let input = getNativeInterface(accDoc, "input"); 84 const errorMessageList = input.getAttributeValue("AXErrorMessageElements"); 85 86 let messages = errorMessageList.map(e => 87 e.getAttributeValue("AXDOMIdentifier") 88 ); 89 messages.sort(); 90 91 is( 92 messages.length, 93 2, 94 "input#input references two elements via 'aria-errormessage'" 95 ); 96 is( 97 messages[0], 98 "error-msg-10charlong", 99 "We expect all elements listed in 'aria-errormessage'" 100 ); 101 is( 102 messages[1], 103 "error-msg-specialchar", 104 "We expect all elements listed in 'aria-errormessage'" 105 ); 106 } 107 ); 108 109 /** 110 * When aria-invalid is set to "false", attribute is missing or without a value, AXErrorMessageElements should 111 * not return associated error messages. 112 * This test verifies that in this cases, AXErrorMessageElements returns `null`. 113 */ 114 addAccessibleTask( 115 ` 116 <label for="input">Field with error</label><input id="input-invalid-false" aria-invalid="false" aria-errormessage="error-msg-specialchar error-msg-10charlong"> 117 <label for="input">Field with error</label><input id="input-invalid-missing" aria-errormessage="error-msg-specialchar error-msg-10charlong"> 118 <label for="input">Field with error</label><input id="input-invalid-spelling-error" aria-invalid aria-errormessage="error-msg-specialchar error-msg-10charlong"> 119 <div id="error-msg-specialchar">Field must contain special characters</div> 120 <div id="error-msg-10charlong">Field must contain more than 10 characters</div> 121 `, 122 (_browser, accDoc) => { 123 const errorsForInvalidFalse = getNativeInterface( 124 accDoc, 125 "input-invalid-false" 126 ).getAttributeValue("AXErrorMessageElements"); 127 128 is( 129 errorsForInvalidFalse, 130 null, 131 "When aria-invalid is 'false', [AXErrorMessageElements] should return null" 132 ); 133 134 const errorsForInvalidMissing = getNativeInterface( 135 accDoc, 136 "input-invalid-missing" 137 ).getAttributeValue("AXErrorMessageElements"); 138 139 is( 140 errorsForInvalidMissing, 141 null, 142 "When aria-invalid is missing, [AXErrorMessageElements] should return null" 143 ); 144 145 const errorsForSpellingError = getNativeInterface( 146 accDoc, 147 "input-invalid-spelling-error" 148 ).getAttributeValue("AXErrorMessageElements"); 149 150 is( 151 errorsForSpellingError, 152 null, 153 "When aria-invalid is provided without value, [AXErrorMessageElements] should return null" 154 ); 155 } 156 ); 157 158 /** 159 * This test modifies the innerText of an associated error message and verifies the correct event AXValidationErrorChagned is fired. 160 */ 161 addAccessibleTask( 162 ` 163 <label for="input">Field with error</label><input id="input" aria-invalid="true" aria-errormessage="error-msg"> 164 <div id="error-msg">Field validation failed</div> 165 `, 166 async (browser, _accDoc) => { 167 let validationErrorChanged = waitForMacEvent("AXValidationErrorChanged"); 168 await SpecialPowers.spawn(browser, [], () => { 169 content.document.getElementById("error-msg").innerText = 170 "new error message"; 171 }); 172 await validationErrorChanged; 173 info("validationErrorChanged: event has arrived"); 174 } 175 ); 176 177 /** 178 * This test modifies the inner tree of an associated error message and verifies the correct event AXValidationErrorChagned is fired. 179 */ 180 addAccessibleTask( 181 ` 182 <label for="input">Field with error</label><input id="input" aria-invalid="true" aria-errormessage="error-msg"> 183 <div id="error-msg">Field validation failed <span id="inner-error-msg"></span></div> 184 `, 185 async (browser, _accDoc) => { 186 let validationErrorChanged = waitForMacEvent("AXValidationErrorChanged"); 187 188 info("validationErrorChanged: changing inner element"); 189 await SpecialPowers.spawn(browser, [], () => { 190 content.document.getElementById("inner-error-msg").innerText = 191 "detailed error message"; 192 }); 193 194 await validationErrorChanged; 195 196 info("validationErrorChanged: event has arrived"); 197 } 198 ); 199 200 /** 201 * When the value of `aria-errormessage` is changed, AXValidationErrorChanged should be triggered. 202 * The test removes the element id from `aria-errormessage` and checks that: 203 * - the event was fired 204 * - AXErrorMessageElements does not return error messages 205 * 206 * Then, the test inserts element id back to `aria-errormessage` and checks that: 207 * - the event AXValidationErrorChanged was fired 208 * - AXErrorMessageElements contain our error message 209 */ 210 addAccessibleTask( 211 ` 212 <label for="input">Field with error</label><input id="input" aria-invalid="true" aria-errormessage="error-msg"> 213 <div id="error-msg">Field validation failed</div> 214 `, 215 async (browser, accDoc) => { 216 let validationErrorChanged = waitForMacEvent("AXValidationErrorChanged"); 217 218 info("validationErrorChanged: removing reference to error"); 219 await SpecialPowers.spawn(browser, [], () => { 220 content.document 221 .getElementById("input") 222 .setAttribute("aria-errormessage", ""); 223 }); 224 225 await validationErrorChanged; 226 227 info("validationErrorChanged: event has arrived"); 228 229 let validationErrors = getNativeInterface( 230 accDoc, 231 "input" 232 ).getAttributeValue("AXErrorMessageElements"); 233 234 is( 235 validationErrors, 236 null, 237 "We have removed reference to error message, AXErrorMessageElements should now contain nothing" 238 ); 239 240 info("validationErrorChanged: adding the reference back"); 241 validationErrorChanged = waitForMacEvent("AXValidationErrorChanged"); 242 243 await SpecialPowers.spawn(browser, [], () => { 244 content.document 245 .getElementById("input") 246 .setAttribute("aria-errormessage", "error-msg"); 247 }); 248 249 await validationErrorChanged; 250 251 validationErrors = getNativeInterface(accDoc, "input") 252 .getAttributeValue("AXErrorMessageElements") 253 .map(e => e.getAttributeValue("AXDOMIdentifier")); 254 255 info("validation errors: " + JSON.stringify(validationErrors)); 256 257 is( 258 validationErrors.length, 259 1, 260 "Reference to 'error-msg' was returned back" 261 ); 262 is( 263 validationErrors[0], 264 "error-msg", 265 "Reference to 'error-msg' was returned back" 266 ); 267 } 268 ); 269 270 /** 271 * This test modifies the innerText of an associated error message on an 272 * input with aria-invalid=false and verifies the error change event 273 * is NOT fired. 274 */ 275 addAccessibleTask( 276 ` 277 <label for="input">Valid field with associated error error</label><input id="input" aria-invalid="false" aria-errormessage="error-msg"> 278 <div id="error-msg">Field validation failed</div> 279 `, 280 async (browser, _accDoc) => { 281 // XXX: We don't have a way to await unexpected, non-core events, so we 282 // use the core EVENT_ERRORMESSAGE_CHANGED here as a proxy for AXValidationErrorChanged 283 const unexpectedEvents = { unexpected: [[EVENT_ERRORMESSAGE_CHANGED]] }; 284 info("Setting new error message text"); 285 await contentSpawnMutation(browser, unexpectedEvents, function () { 286 content.document.getElementById("error-msg").innerText = 287 "new error message"; 288 }); 289 ok(true, "Did not receive error message event!"); 290 } 291 );