accessible/tests/mochitest/jsat/jsatcommon.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // A common module to run tests on the AccessFu module
michael@0 2
michael@0 3 'use strict';
michael@0 4
michael@0 5 /*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
michael@0 6 AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
michael@0 7
michael@0 8 /**
michael@0 9 * A global variable holding an array of test functions.
michael@0 10 */
michael@0 11 var gTestFuncs = [];
michael@0 12 /**
michael@0 13 * A global Iterator for the array of test functions.
michael@0 14 */
michael@0 15 var gIterator;
michael@0 16
michael@0 17 Components.utils.import('resource://gre/modules/Services.jsm');
michael@0 18 Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
michael@0 19 Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
michael@0 20 Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
michael@0 21
michael@0 22 const dwellThreshold = GestureSettings.dwellThreshold;
michael@0 23 const swipeMaxDuration = GestureSettings.swipeMaxDuration;
michael@0 24 const maxConsecutiveGestureDelay = GestureSettings.maxConsecutiveGestureDelay;
michael@0 25
michael@0 26 // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
michael@0 27 // SimpleTest.executeSoon timeout is bigger than the timer settings in
michael@0 28 // GestureSettings that causes intermittents.
michael@0 29 GestureSettings.dwellThreshold = dwellThreshold * 10;
michael@0 30 GestureSettings.swipeMaxDuration = swipeMaxDuration * 10;
michael@0 31 GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay * 10;
michael@0 32
michael@0 33 var AccessFuTest = {
michael@0 34
michael@0 35 addFunc: function AccessFuTest_addFunc(aFunc) {
michael@0 36 if (aFunc) {
michael@0 37 gTestFuncs.push(aFunc);
michael@0 38 }
michael@0 39 },
michael@0 40
michael@0 41 _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
michael@0 42 var listener = {
michael@0 43 observe: function observe(aMessage) {
michael@0 44 // Ignore unexpected messages.
michael@0 45 if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
michael@0 46 return;
michael@0 47 }
michael@0 48 if (aMessage.message.indexOf(aWaitForMessage) < 0) {
michael@0 49 return;
michael@0 50 }
michael@0 51 aListenerFunc.apply(listener);
michael@0 52 }
michael@0 53 };
michael@0 54 Services.console.registerListener(listener);
michael@0 55 return listener;
michael@0 56 },
michael@0 57
michael@0 58 on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
michael@0 59 return this._registerListener(aWaitForMessage, aListenerFunc);
michael@0 60 },
michael@0 61
michael@0 62 off_log: function AccessFuTest_off_log(aListener) {
michael@0 63 Services.console.unregisterListener(aListener);
michael@0 64 },
michael@0 65
michael@0 66 once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
michael@0 67 return this._registerListener(aWaitForMessage,
michael@0 68 function listenAndUnregister() {
michael@0 69 Services.console.unregisterListener(this);
michael@0 70 aListenerFunc();
michael@0 71 });
michael@0 72 },
michael@0 73
michael@0 74 _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
michael@0 75 var listener = function listener(aSubject, aTopic, aData) {
michael@0 76 var data = JSON.parse(aData)[1];
michael@0 77 // Ignore non-relevant outputs.
michael@0 78 if (!data) {
michael@0 79 return;
michael@0 80 }
michael@0 81 isDeeply(data.details.actions, aWaitForData, "Data is correct");
michael@0 82 aListener.apply(listener);
michael@0 83 };
michael@0 84 Services.obs.addObserver(listener, 'accessfu-output', false);
michael@0 85 return listener;
michael@0 86 },
michael@0 87
michael@0 88 on: function AccessFuTest_on(aWaitForData, aListener) {
michael@0 89 return this._addObserver(aWaitForData, aListener);
michael@0 90 },
michael@0 91
michael@0 92 off: function AccessFuTest_off(aListener) {
michael@0 93 Services.obs.removeObserver(aListener, 'accessfu-output');
michael@0 94 },
michael@0 95
michael@0 96 once: function AccessFuTest_once(aWaitForData, aListener) {
michael@0 97 return this._addObserver(aWaitForData, function observerAndRemove() {
michael@0 98 Services.obs.removeObserver(this, 'accessfu-output');
michael@0 99 aListener();
michael@0 100 });
michael@0 101 },
michael@0 102
michael@0 103 _waitForExplicitFinish: false,
michael@0 104
michael@0 105 waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
michael@0 106 this._waitForExplicitFinish = true;
michael@0 107 },
michael@0 108
michael@0 109 finish: function AccessFuTest_finish() {
michael@0 110 // Disable the console service logging.
michael@0 111 Logger.test = false;
michael@0 112 Logger.logLevel = Logger.INFO;
michael@0 113 // Reset Gesture Settings.
michael@0 114 GestureSettings.dwellThreshold = dwellThreshold;
michael@0 115 GestureSettings.swipeMaxDuration = swipeMaxDuration;
michael@0 116 GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay;
michael@0 117 // Finish through idle callback to let AccessFu._disable complete.
michael@0 118 SimpleTest.executeSoon(function () {
michael@0 119 AccessFu.detach();
michael@0 120 SimpleTest.finish();
michael@0 121 });
michael@0 122 },
michael@0 123
michael@0 124 nextTest: function AccessFuTest_nextTest() {
michael@0 125 var testFunc;
michael@0 126 try {
michael@0 127 // Get the next test function from the iterator. If none left,
michael@0 128 // StopIteration exception is thrown.
michael@0 129 testFunc = gIterator.next()[1];
michael@0 130 } catch (ex) {
michael@0 131 // StopIteration exception.
michael@0 132 this.finish();
michael@0 133 return;
michael@0 134 }
michael@0 135 testFunc();
michael@0 136 },
michael@0 137
michael@0 138 runTests: function AccessFuTest_runTests() {
michael@0 139 if (gTestFuncs.length === 0) {
michael@0 140 ok(false, "No tests specified!");
michael@0 141 SimpleTest.finish();
michael@0 142 return;
michael@0 143 }
michael@0 144
michael@0 145 // Create an Iterator for gTestFuncs array.
michael@0 146 gIterator = Iterator(gTestFuncs); // jshint ignore:line
michael@0 147
michael@0 148 // Start AccessFu and put it in stand-by.
michael@0 149 Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
michael@0 150
michael@0 151 AccessFu.attach(getMainChromeWindow(window));
michael@0 152
michael@0 153 AccessFu.readyCallback = function readyCallback() {
michael@0 154 // Enable logging to the console service.
michael@0 155 Logger.test = true;
michael@0 156 Logger.logLevel = Logger.DEBUG;
michael@0 157 };
michael@0 158
michael@0 159 SpecialPowers.pushPrefEnv({
michael@0 160 'set': [['accessibility.accessfu.notify_output', 1],
michael@0 161 ['dom.mozSettings.enabled', true]]
michael@0 162 }, function () {
michael@0 163 if (AccessFuTest._waitForExplicitFinish) {
michael@0 164 // Run all test functions asynchronously.
michael@0 165 AccessFuTest.nextTest();
michael@0 166 } else {
michael@0 167 // Run all test functions synchronously.
michael@0 168 [testFunc() for (testFunc of gTestFuncs)]; // jshint ignore:line
michael@0 169 AccessFuTest.finish();
michael@0 170 }
michael@0 171 });
michael@0 172 }
michael@0 173 };
michael@0 174
michael@0 175 function AccessFuContentTest(aFuncResultPairs) {
michael@0 176 this.queue = aFuncResultPairs;
michael@0 177 }
michael@0 178
michael@0 179 AccessFuContentTest.prototype = {
michael@0 180 currentPair: null,
michael@0 181
michael@0 182 start: function(aFinishedCallback) {
michael@0 183 Logger.logLevel = Logger.DEBUG;
michael@0 184 this.finishedCallback = aFinishedCallback;
michael@0 185 var self = this;
michael@0 186
michael@0 187 // Get top content message manager, and set it up.
michael@0 188 this.mms = [Utils.getMessageManager(currentBrowser())];
michael@0 189 this.setupMessageManager(this.mms[0], function () {
michael@0 190 // Get child message managers and set them up
michael@0 191 var frames = currentTabDocument().querySelectorAll('iframe');
michael@0 192 if (frames.length === 0) {
michael@0 193 self.pump();
michael@0 194 return;
michael@0 195 }
michael@0 196
michael@0 197 var toSetup = 0;
michael@0 198 for (var i = 0; i < frames.length; i++ ) {
michael@0 199 var mm = Utils.getMessageManager(frames[i]);
michael@0 200 if (mm) {
michael@0 201 toSetup++;
michael@0 202 self.mms.push(mm);
michael@0 203 self.setupMessageManager(mm, function () {
michael@0 204 if (--toSetup === 0) {
michael@0 205 // All message managers are loaded and ready to go.
michael@0 206 self.pump();
michael@0 207 }
michael@0 208 });
michael@0 209 }
michael@0 210 }
michael@0 211 });
michael@0 212 },
michael@0 213
michael@0 214 finish: function() {
michael@0 215 Logger.logLevel = Logger.INFO;
michael@0 216 for (var mm of this.mms) {
michael@0 217 mm.sendAsyncMessage('AccessFu:Stop');
michael@0 218 }
michael@0 219 if (this.finishedCallback) {
michael@0 220 this.finishedCallback();
michael@0 221 }
michael@0 222 },
michael@0 223
michael@0 224 setupMessageManager: function (aMessageManager, aCallback) {
michael@0 225 function contentScript() {
michael@0 226 addMessageListener('AccessFuTest:Focus', function (aMessage) {
michael@0 227 var elem = content.document.querySelector(aMessage.json.selector);
michael@0 228 if (elem) {
michael@0 229 if (aMessage.json.blur) {
michael@0 230 elem.blur();
michael@0 231 } else {
michael@0 232 elem.focus();
michael@0 233 }
michael@0 234 }
michael@0 235 });
michael@0 236 }
michael@0 237
michael@0 238 aMessageManager.addMessageListener('AccessFu:Present', this);
michael@0 239 aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
michael@0 240 aMessageManager.addMessageListener('AccessFu:Ready', function () {
michael@0 241 aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
michael@0 242 aMessageManager.sendAsyncMessage('AccessFu:Start',
michael@0 243 { buildApp: 'browser',
michael@0 244 androidSdkVersion: Utils.AndroidSdkVersion,
michael@0 245 logLevel: 'DEBUG' });
michael@0 246 });
michael@0 247
michael@0 248 aMessageManager.loadFrameScript(
michael@0 249 'chrome://global/content/accessibility/content-script.js', false);
michael@0 250 aMessageManager.loadFrameScript(
michael@0 251 'data:,(' + contentScript.toString() + ')();', false);
michael@0 252 },
michael@0 253
michael@0 254 pump: function() {
michael@0 255 this.currentPair = this.queue.shift();
michael@0 256
michael@0 257 if (this.currentPair) {
michael@0 258 if (this.currentPair[0] instanceof Function) {
michael@0 259 this.currentPair[0](this.mms[0]);
michael@0 260 } else if (this.currentPair[0]) {
michael@0 261 this.mms[0].sendAsyncMessage(this.currentPair[0].name,
michael@0 262 this.currentPair[0].json);
michael@0 263 }
michael@0 264
michael@0 265 if (!this.currentPair[1]) {
michael@0 266 this.pump();
michael@0 267 }
michael@0 268 } else {
michael@0 269 this.finish();
michael@0 270 }
michael@0 271 },
michael@0 272
michael@0 273 receiveMessage: function(aMessage) {
michael@0 274 if (!this.currentPair) {
michael@0 275 return;
michael@0 276 }
michael@0 277
michael@0 278 var expected = this.currentPair[1] || {};
michael@0 279
michael@0 280 // |expected| can simply be a name of a message, no more further testing.
michael@0 281 if (aMessage.name === expected) {
michael@0 282 ok(true, 'Received ' + expected);
michael@0 283 this.pump();
michael@0 284 return;
michael@0 285 }
michael@0 286
michael@0 287 var speech = this.extractUtterance(aMessage.json);
michael@0 288 var android = this.extractAndroid(aMessage.json, expected.android);
michael@0 289 if ((speech && expected.speak) || (android && expected.android)) {
michael@0 290 if (expected.speak) {
michael@0 291 (SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak,
michael@0 292 '"' + speech + '" spoken');
michael@0 293 }
michael@0 294
michael@0 295 if (expected.android) {
michael@0 296 var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
michael@0 297 checkFunc.apply(SimpleTest,
michael@0 298 this.lazyCompare(android, expected.android));
michael@0 299 }
michael@0 300
michael@0 301 this.pump();
michael@0 302 }
michael@0 303
michael@0 304 },
michael@0 305
michael@0 306 lazyCompare: function lazyCompare(aReceived, aExpected) {
michael@0 307 var matches = true;
michael@0 308 var delta = [];
michael@0 309 for (var attr in aExpected) {
michael@0 310 var expected = aExpected[attr];
michael@0 311 var received = aReceived !== undefined ? aReceived[attr] : null;
michael@0 312 if (typeof expected === 'object') {
michael@0 313 var [childMatches, childDelta] = this.lazyCompare(received, expected);
michael@0 314 if (!childMatches) {
michael@0 315 delta.push(attr + ' [ ' + childDelta + ' ]');
michael@0 316 matches = false;
michael@0 317 }
michael@0 318 } else {
michael@0 319 if (received !== expected) {
michael@0 320 delta.push(
michael@0 321 attr + ' [ expected ' + expected + ' got ' + received + ' ]');
michael@0 322 matches = false;
michael@0 323 }
michael@0 324 }
michael@0 325 }
michael@0 326 return [matches, delta.join(' ')];
michael@0 327 },
michael@0 328
michael@0 329 extractUtterance: function(aData) {
michael@0 330 if (!aData) {
michael@0 331 return null;
michael@0 332 }
michael@0 333
michael@0 334 for (var output of aData) {
michael@0 335 if (output && output.type === 'Speech') {
michael@0 336 for (var action of output.details.actions) {
michael@0 337 if (action && action.method == 'speak') {
michael@0 338 return action.data;
michael@0 339 }
michael@0 340 }
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 return null;
michael@0 345 },
michael@0 346
michael@0 347 extractAndroid: function(aData, aExpectedEvents) {
michael@0 348 for (var output of aData) {
michael@0 349 if (output && output.type === 'Android') {
michael@0 350 for (var i in output.details) {
michael@0 351 // Only extract if event types match expected event types.
michael@0 352 var exp = aExpectedEvents ? aExpectedEvents[i] : null;
michael@0 353 if (!exp || (output.details[i].eventType !== exp.eventType)) {
michael@0 354 return null;
michael@0 355 }
michael@0 356 }
michael@0 357 return output.details;
michael@0 358 }
michael@0 359 }
michael@0 360
michael@0 361 return null;
michael@0 362 }
michael@0 363 };
michael@0 364
michael@0 365 // Common content messages
michael@0 366
michael@0 367 var ContentMessages = {
michael@0 368 simpleMoveFirst: {
michael@0 369 name: 'AccessFu:MoveCursor',
michael@0 370 json: {
michael@0 371 action: 'moveFirst',
michael@0 372 rule: 'Simple',
michael@0 373 inputType: 'gesture',
michael@0 374 origin: 'top'
michael@0 375 }
michael@0 376 },
michael@0 377
michael@0 378 simpleMoveLast: {
michael@0 379 name: 'AccessFu:MoveCursor',
michael@0 380 json: {
michael@0 381 action: 'moveLast',
michael@0 382 rule: 'Simple',
michael@0 383 inputType: 'gesture',
michael@0 384 origin: 'top'
michael@0 385 }
michael@0 386 },
michael@0 387
michael@0 388 simpleMoveNext: {
michael@0 389 name: 'AccessFu:MoveCursor',
michael@0 390 json: {
michael@0 391 action: 'moveNext',
michael@0 392 rule: 'Simple',
michael@0 393 inputType: 'gesture',
michael@0 394 origin: 'top'
michael@0 395 }
michael@0 396 },
michael@0 397
michael@0 398 simpleMovePrevious: {
michael@0 399 name: 'AccessFu:MoveCursor',
michael@0 400 json: {
michael@0 401 action: 'movePrevious',
michael@0 402 rule: 'Simple',
michael@0 403 inputType: 'gesture',
michael@0 404 origin: 'top'
michael@0 405 }
michael@0 406 },
michael@0 407
michael@0 408 clearCursor: {
michael@0 409 name: 'AccessFu:ClearCursor',
michael@0 410 json: {
michael@0 411 origin: 'top'
michael@0 412 }
michael@0 413 },
michael@0 414
michael@0 415 adjustRangeUp: {
michael@0 416 name: 'AccessFu:AdjustRange',
michael@0 417 json: {
michael@0 418 origin: 'top',
michael@0 419 direction: 'backward'
michael@0 420 }
michael@0 421 },
michael@0 422
michael@0 423 adjustRangeDown: {
michael@0 424 name: 'AccessFu:AdjustRange',
michael@0 425 json: {
michael@0 426 origin: 'top',
michael@0 427 direction: 'forward'
michael@0 428 }
michael@0 429 },
michael@0 430
michael@0 431 focusSelector: function focusSelector(aSelector, aBlur) {
michael@0 432 return {
michael@0 433 name: 'AccessFuTest:Focus',
michael@0 434 json: {
michael@0 435 selector: aSelector,
michael@0 436 blur: aBlur
michael@0 437 }
michael@0 438 };
michael@0 439 },
michael@0 440
michael@0 441 activateCurrent: function activateCurrent(aOffset) {
michael@0 442 return {
michael@0 443 name: 'AccessFu:Activate',
michael@0 444 json: {
michael@0 445 origin: 'top',
michael@0 446 offset: aOffset
michael@0 447 }
michael@0 448 };
michael@0 449 },
michael@0 450
michael@0 451 moveNextBy: function moveNextBy(aGranularity) {
michael@0 452 return {
michael@0 453 name: 'AccessFu:MoveByGranularity',
michael@0 454 json: {
michael@0 455 direction: 'Next',
michael@0 456 granularity: this._granularityMap[aGranularity]
michael@0 457 }
michael@0 458 };
michael@0 459 },
michael@0 460
michael@0 461 movePreviousBy: function movePreviousBy(aGranularity) {
michael@0 462 return {
michael@0 463 name: 'AccessFu:MoveByGranularity',
michael@0 464 json: {
michael@0 465 direction: 'Previous',
michael@0 466 granularity: this._granularityMap[aGranularity]
michael@0 467 }
michael@0 468 };
michael@0 469 },
michael@0 470
michael@0 471 moveCaretNextBy: function moveCaretNextBy(aGranularity) {
michael@0 472 return {
michael@0 473 name: 'AccessFu:MoveCaret',
michael@0 474 json: {
michael@0 475 direction: 'Next',
michael@0 476 granularity: this._granularityMap[aGranularity]
michael@0 477 }
michael@0 478 };
michael@0 479 },
michael@0 480
michael@0 481 moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
michael@0 482 return {
michael@0 483 name: 'AccessFu:MoveCaret',
michael@0 484 json: {
michael@0 485 direction: 'Previous',
michael@0 486 granularity: this._granularityMap[aGranularity]
michael@0 487 }
michael@0 488 };
michael@0 489 },
michael@0 490
michael@0 491 _granularityMap: {
michael@0 492 'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
michael@0 493 'word': 2, // MOVEMENT_GRANULARITY_WORD
michael@0 494 'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
michael@0 495 }
michael@0 496 };
michael@0 497
michael@0 498 var AndroidEvent = {
michael@0 499 VIEW_CLICKED: 0x01,
michael@0 500 VIEW_LONG_CLICKED: 0x02,
michael@0 501 VIEW_SELECTED: 0x04,
michael@0 502 VIEW_FOCUSED: 0x08,
michael@0 503 VIEW_TEXT_CHANGED: 0x10,
michael@0 504 WINDOW_STATE_CHANGED: 0x20,
michael@0 505 VIEW_HOVER_ENTER: 0x80,
michael@0 506 VIEW_HOVER_EXIT: 0x100,
michael@0 507 VIEW_SCROLLED: 0x1000,
michael@0 508 VIEW_TEXT_SELECTION_CHANGED: 0x2000,
michael@0 509 ANNOUNCEMENT: 0x4000,
michael@0 510 VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
michael@0 511 VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
michael@0 512 };

mercurial