accessible/tests/mochitest/jsat/jsatcommon.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/accessible/tests/mochitest/jsat/jsatcommon.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,512 @@
     1.4 +// A common module to run tests on the AccessFu module
     1.5 +
     1.6 +'use strict';
     1.7 +
     1.8 +/*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
     1.9 +  AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
    1.10 +
    1.11 +/**
    1.12 +  * A global variable holding an array of test functions.
    1.13 +  */
    1.14 +var gTestFuncs = [];
    1.15 +/**
    1.16 +  * A global Iterator for the array of test functions.
    1.17 +  */
    1.18 +var gIterator;
    1.19 +
    1.20 +Components.utils.import('resource://gre/modules/Services.jsm');
    1.21 +Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
    1.22 +Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
    1.23 +Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
    1.24 +
    1.25 +const dwellThreshold = GestureSettings.dwellThreshold;
    1.26 +const swipeMaxDuration = GestureSettings.swipeMaxDuration;
    1.27 +const maxConsecutiveGestureDelay = GestureSettings.maxConsecutiveGestureDelay;
    1.28 +
    1.29 +// https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
    1.30 +// SimpleTest.executeSoon timeout is bigger than the timer settings in
    1.31 +// GestureSettings that causes intermittents.
    1.32 +GestureSettings.dwellThreshold = dwellThreshold * 10;
    1.33 +GestureSettings.swipeMaxDuration = swipeMaxDuration * 10;
    1.34 +GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay * 10;
    1.35 +
    1.36 +var AccessFuTest = {
    1.37 +
    1.38 +  addFunc: function AccessFuTest_addFunc(aFunc) {
    1.39 +    if (aFunc) {
    1.40 +      gTestFuncs.push(aFunc);
    1.41 +    }
    1.42 +  },
    1.43 +
    1.44 +  _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
    1.45 +    var listener = {
    1.46 +      observe: function observe(aMessage) {
    1.47 +        // Ignore unexpected messages.
    1.48 +        if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
    1.49 +          return;
    1.50 +        }
    1.51 +        if (aMessage.message.indexOf(aWaitForMessage) < 0) {
    1.52 +          return;
    1.53 +        }
    1.54 +        aListenerFunc.apply(listener);
    1.55 +      }
    1.56 +    };
    1.57 +    Services.console.registerListener(listener);
    1.58 +    return listener;
    1.59 +  },
    1.60 +
    1.61 +  on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
    1.62 +    return this._registerListener(aWaitForMessage, aListenerFunc);
    1.63 +  },
    1.64 +
    1.65 +  off_log: function AccessFuTest_off_log(aListener) {
    1.66 +    Services.console.unregisterListener(aListener);
    1.67 +  },
    1.68 +
    1.69 +  once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
    1.70 +    return this._registerListener(aWaitForMessage,
    1.71 +      function listenAndUnregister() {
    1.72 +        Services.console.unregisterListener(this);
    1.73 +        aListenerFunc();
    1.74 +      });
    1.75 +  },
    1.76 +
    1.77 +  _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
    1.78 +    var listener = function listener(aSubject, aTopic, aData) {
    1.79 +      var data = JSON.parse(aData)[1];
    1.80 +      // Ignore non-relevant outputs.
    1.81 +      if (!data) {
    1.82 +        return;
    1.83 +      }
    1.84 +      isDeeply(data.details.actions, aWaitForData, "Data is correct");
    1.85 +      aListener.apply(listener);
    1.86 +    };
    1.87 +    Services.obs.addObserver(listener, 'accessfu-output', false);
    1.88 +    return listener;
    1.89 +  },
    1.90 +
    1.91 +  on: function AccessFuTest_on(aWaitForData, aListener) {
    1.92 +    return this._addObserver(aWaitForData, aListener);
    1.93 +  },
    1.94 +
    1.95 +  off: function AccessFuTest_off(aListener) {
    1.96 +    Services.obs.removeObserver(aListener, 'accessfu-output');
    1.97 +  },
    1.98 +
    1.99 +  once: function AccessFuTest_once(aWaitForData, aListener) {
   1.100 +    return this._addObserver(aWaitForData, function observerAndRemove() {
   1.101 +      Services.obs.removeObserver(this, 'accessfu-output');
   1.102 +      aListener();
   1.103 +    });
   1.104 +  },
   1.105 +
   1.106 +  _waitForExplicitFinish: false,
   1.107 +
   1.108 +  waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
   1.109 +    this._waitForExplicitFinish = true;
   1.110 +  },
   1.111 +
   1.112 +  finish: function AccessFuTest_finish() {
   1.113 +    // Disable the console service logging.
   1.114 +    Logger.test = false;
   1.115 +    Logger.logLevel = Logger.INFO;
   1.116 +    // Reset Gesture Settings.
   1.117 +    GestureSettings.dwellThreshold = dwellThreshold;
   1.118 +    GestureSettings.swipeMaxDuration = swipeMaxDuration;
   1.119 +    GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay;
   1.120 +    // Finish through idle callback to let AccessFu._disable complete.
   1.121 +    SimpleTest.executeSoon(function () {
   1.122 +      AccessFu.detach();
   1.123 +      SimpleTest.finish();
   1.124 +    });
   1.125 +  },
   1.126 +
   1.127 +  nextTest: function AccessFuTest_nextTest() {
   1.128 +    var testFunc;
   1.129 +    try {
   1.130 +      // Get the next test function from the iterator. If none left,
   1.131 +      // StopIteration exception is thrown.
   1.132 +      testFunc = gIterator.next()[1];
   1.133 +    } catch (ex) {
   1.134 +      // StopIteration exception.
   1.135 +      this.finish();
   1.136 +      return;
   1.137 +    }
   1.138 +    testFunc();
   1.139 +  },
   1.140 +
   1.141 +  runTests: function AccessFuTest_runTests() {
   1.142 +    if (gTestFuncs.length === 0) {
   1.143 +      ok(false, "No tests specified!");
   1.144 +      SimpleTest.finish();
   1.145 +      return;
   1.146 +    }
   1.147 +
   1.148 +    // Create an Iterator for gTestFuncs array.
   1.149 +    gIterator = Iterator(gTestFuncs); // jshint ignore:line
   1.150 +
   1.151 +    // Start AccessFu and put it in stand-by.
   1.152 +    Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
   1.153 +
   1.154 +    AccessFu.attach(getMainChromeWindow(window));
   1.155 +
   1.156 +    AccessFu.readyCallback = function readyCallback() {
   1.157 +      // Enable logging to the console service.
   1.158 +      Logger.test = true;
   1.159 +      Logger.logLevel = Logger.DEBUG;
   1.160 +    };
   1.161 +
   1.162 +    SpecialPowers.pushPrefEnv({
   1.163 +      'set': [['accessibility.accessfu.notify_output', 1],
   1.164 +              ['dom.mozSettings.enabled', true]]
   1.165 +    }, function () {
   1.166 +      if (AccessFuTest._waitForExplicitFinish) {
   1.167 +        // Run all test functions asynchronously.
   1.168 +        AccessFuTest.nextTest();
   1.169 +      } else {
   1.170 +        // Run all test functions synchronously.
   1.171 +        [testFunc() for (testFunc of gTestFuncs)]; // jshint ignore:line
   1.172 +        AccessFuTest.finish();
   1.173 +      }
   1.174 +    });
   1.175 +  }
   1.176 +};
   1.177 +
   1.178 +function AccessFuContentTest(aFuncResultPairs) {
   1.179 +  this.queue = aFuncResultPairs;
   1.180 +}
   1.181 +
   1.182 +AccessFuContentTest.prototype = {
   1.183 +  currentPair: null,
   1.184 +
   1.185 +  start: function(aFinishedCallback) {
   1.186 +    Logger.logLevel = Logger.DEBUG;
   1.187 +    this.finishedCallback = aFinishedCallback;
   1.188 +    var self = this;
   1.189 +
   1.190 +    // Get top content message manager, and set it up.
   1.191 +    this.mms = [Utils.getMessageManager(currentBrowser())];
   1.192 +    this.setupMessageManager(this.mms[0], function () {
   1.193 +      // Get child message managers and set them up
   1.194 +      var frames = currentTabDocument().querySelectorAll('iframe');
   1.195 +      if (frames.length === 0) {
   1.196 +        self.pump();
   1.197 +        return;
   1.198 +      }
   1.199 +
   1.200 +      var toSetup = 0;
   1.201 +      for (var i = 0; i < frames.length; i++ ) {
   1.202 +        var mm = Utils.getMessageManager(frames[i]);
   1.203 +        if (mm) {
   1.204 +          toSetup++;
   1.205 +          self.mms.push(mm);
   1.206 +          self.setupMessageManager(mm, function () {
   1.207 +            if (--toSetup === 0) {
   1.208 +              // All message managers are loaded and ready to go.
   1.209 +              self.pump();
   1.210 +            }
   1.211 +          });
   1.212 +        }
   1.213 +      }
   1.214 +    });
   1.215 +  },
   1.216 +
   1.217 +  finish: function() {
   1.218 +    Logger.logLevel = Logger.INFO;
   1.219 +    for (var mm of this.mms) {
   1.220 +        mm.sendAsyncMessage('AccessFu:Stop');
   1.221 +      }
   1.222 +    if (this.finishedCallback) {
   1.223 +      this.finishedCallback();
   1.224 +    }
   1.225 +  },
   1.226 +
   1.227 +  setupMessageManager:  function (aMessageManager, aCallback) {
   1.228 +    function contentScript() {
   1.229 +      addMessageListener('AccessFuTest:Focus', function (aMessage) {
   1.230 +        var elem = content.document.querySelector(aMessage.json.selector);
   1.231 +        if (elem) {
   1.232 +          if (aMessage.json.blur) {
   1.233 +            elem.blur();
   1.234 +          } else {
   1.235 +            elem.focus();
   1.236 +          }
   1.237 +        }
   1.238 +      });
   1.239 +    }
   1.240 +
   1.241 +    aMessageManager.addMessageListener('AccessFu:Present', this);
   1.242 +    aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
   1.243 +    aMessageManager.addMessageListener('AccessFu:Ready', function () {
   1.244 +      aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
   1.245 +      aMessageManager.sendAsyncMessage('AccessFu:Start',
   1.246 +        { buildApp: 'browser',
   1.247 +          androidSdkVersion: Utils.AndroidSdkVersion,
   1.248 +          logLevel: 'DEBUG' });
   1.249 +    });
   1.250 +
   1.251 +    aMessageManager.loadFrameScript(
   1.252 +      'chrome://global/content/accessibility/content-script.js', false);
   1.253 +    aMessageManager.loadFrameScript(
   1.254 +      'data:,(' + contentScript.toString() + ')();', false);
   1.255 +  },
   1.256 +
   1.257 +  pump: function() {
   1.258 +    this.currentPair = this.queue.shift();
   1.259 +
   1.260 +    if (this.currentPair) {
   1.261 +      if (this.currentPair[0] instanceof Function) {
   1.262 +        this.currentPair[0](this.mms[0]);
   1.263 +      } else if (this.currentPair[0]) {
   1.264 +        this.mms[0].sendAsyncMessage(this.currentPair[0].name,
   1.265 +         this.currentPair[0].json);
   1.266 +      }
   1.267 +
   1.268 +      if (!this.currentPair[1]) {
   1.269 +       this.pump();
   1.270 +     }
   1.271 +    } else {
   1.272 +      this.finish();
   1.273 +    }
   1.274 +  },
   1.275 +
   1.276 +  receiveMessage: function(aMessage) {
   1.277 +    if (!this.currentPair) {
   1.278 +      return;
   1.279 +    }
   1.280 +
   1.281 +    var expected = this.currentPair[1] || {};
   1.282 +
   1.283 +    // |expected| can simply be a name of a message, no more further testing.
   1.284 +    if (aMessage.name === expected) {
   1.285 +      ok(true, 'Received ' + expected);
   1.286 +      this.pump();
   1.287 +      return;
   1.288 +    }
   1.289 +
   1.290 +    var speech = this.extractUtterance(aMessage.json);
   1.291 +    var android = this.extractAndroid(aMessage.json, expected.android);
   1.292 +    if ((speech && expected.speak) || (android && expected.android)) {
   1.293 +      if (expected.speak) {
   1.294 +        (SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak,
   1.295 +          '"' + speech + '" spoken');
   1.296 +      }
   1.297 +
   1.298 +      if (expected.android) {
   1.299 +        var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
   1.300 +        checkFunc.apply(SimpleTest,
   1.301 +          this.lazyCompare(android, expected.android));
   1.302 +      }
   1.303 +
   1.304 +      this.pump();
   1.305 +    }
   1.306 +
   1.307 +  },
   1.308 +
   1.309 +  lazyCompare: function lazyCompare(aReceived, aExpected) {
   1.310 +    var matches = true;
   1.311 +    var delta = [];
   1.312 +    for (var attr in aExpected) {
   1.313 +      var expected = aExpected[attr];
   1.314 +      var received = aReceived !== undefined ? aReceived[attr] : null;
   1.315 +      if (typeof expected === 'object') {
   1.316 +        var [childMatches, childDelta] = this.lazyCompare(received, expected);
   1.317 +        if (!childMatches) {
   1.318 +          delta.push(attr + ' [ ' + childDelta + ' ]');
   1.319 +          matches = false;
   1.320 +        }
   1.321 +      } else {
   1.322 +        if (received !== expected) {
   1.323 +          delta.push(
   1.324 +            attr + ' [ expected ' + expected + ' got ' + received + ' ]');
   1.325 +          matches = false;
   1.326 +        }
   1.327 +      }
   1.328 +    }
   1.329 +    return [matches, delta.join(' ')];
   1.330 +  },
   1.331 +
   1.332 +  extractUtterance: function(aData) {
   1.333 +    if (!aData) {
   1.334 +      return null;
   1.335 +    }
   1.336 +
   1.337 +    for (var output of aData) {
   1.338 +      if (output && output.type === 'Speech') {
   1.339 +        for (var action of output.details.actions) {
   1.340 +          if (action && action.method == 'speak') {
   1.341 +            return action.data;
   1.342 +          }
   1.343 +        }
   1.344 +      }
   1.345 +    }
   1.346 +
   1.347 +    return null;
   1.348 +  },
   1.349 +
   1.350 +  extractAndroid: function(aData, aExpectedEvents) {
   1.351 +    for (var output of aData) {
   1.352 +      if (output && output.type === 'Android') {
   1.353 +        for (var i in output.details) {
   1.354 +          // Only extract if event types match expected event types.
   1.355 +          var exp = aExpectedEvents ? aExpectedEvents[i] : null;
   1.356 +          if (!exp || (output.details[i].eventType !== exp.eventType)) {
   1.357 +            return null;
   1.358 +          }
   1.359 +        }
   1.360 +        return output.details;
   1.361 +      }
   1.362 +    }
   1.363 +
   1.364 +    return null;
   1.365 +  }
   1.366 +};
   1.367 +
   1.368 +// Common content messages
   1.369 +
   1.370 +var ContentMessages = {
   1.371 +  simpleMoveFirst: {
   1.372 +    name: 'AccessFu:MoveCursor',
   1.373 +    json: {
   1.374 +      action: 'moveFirst',
   1.375 +      rule: 'Simple',
   1.376 +      inputType: 'gesture',
   1.377 +      origin: 'top'
   1.378 +    }
   1.379 +  },
   1.380 +
   1.381 +  simpleMoveLast: {
   1.382 +    name: 'AccessFu:MoveCursor',
   1.383 +    json: {
   1.384 +      action: 'moveLast',
   1.385 +      rule: 'Simple',
   1.386 +      inputType: 'gesture',
   1.387 +      origin: 'top'
   1.388 +    }
   1.389 +  },
   1.390 +
   1.391 +  simpleMoveNext: {
   1.392 +    name: 'AccessFu:MoveCursor',
   1.393 +    json: {
   1.394 +      action: 'moveNext',
   1.395 +      rule: 'Simple',
   1.396 +      inputType: 'gesture',
   1.397 +      origin: 'top'
   1.398 +    }
   1.399 +  },
   1.400 +
   1.401 +  simpleMovePrevious: {
   1.402 +    name: 'AccessFu:MoveCursor',
   1.403 +    json: {
   1.404 +      action: 'movePrevious',
   1.405 +      rule: 'Simple',
   1.406 +      inputType: 'gesture',
   1.407 +      origin: 'top'
   1.408 +    }
   1.409 +  },
   1.410 +
   1.411 +  clearCursor: {
   1.412 +    name: 'AccessFu:ClearCursor',
   1.413 +    json: {
   1.414 +      origin: 'top'
   1.415 +    }
   1.416 +  },
   1.417 +
   1.418 +  adjustRangeUp: {
   1.419 +    name: 'AccessFu:AdjustRange',
   1.420 +    json: {
   1.421 +      origin: 'top',
   1.422 +      direction: 'backward'
   1.423 +    }
   1.424 +  },
   1.425 +
   1.426 +  adjustRangeDown: {
   1.427 +    name: 'AccessFu:AdjustRange',
   1.428 +    json: {
   1.429 +      origin: 'top',
   1.430 +      direction: 'forward'
   1.431 +    }
   1.432 +  },
   1.433 +
   1.434 +  focusSelector: function focusSelector(aSelector, aBlur) {
   1.435 +    return {
   1.436 +      name: 'AccessFuTest:Focus',
   1.437 +      json: {
   1.438 +        selector: aSelector,
   1.439 +        blur: aBlur
   1.440 +      }
   1.441 +    };
   1.442 +  },
   1.443 +
   1.444 +  activateCurrent: function activateCurrent(aOffset) {
   1.445 +    return {
   1.446 +      name: 'AccessFu:Activate',
   1.447 +      json: {
   1.448 +        origin: 'top',
   1.449 +        offset: aOffset
   1.450 +      }
   1.451 +    };
   1.452 +  },
   1.453 +
   1.454 +  moveNextBy: function moveNextBy(aGranularity) {
   1.455 +    return {
   1.456 +      name: 'AccessFu:MoveByGranularity',
   1.457 +      json: {
   1.458 +        direction: 'Next',
   1.459 +        granularity: this._granularityMap[aGranularity]
   1.460 +      }
   1.461 +    };
   1.462 +  },
   1.463 +
   1.464 +  movePreviousBy: function movePreviousBy(aGranularity) {
   1.465 +    return {
   1.466 +      name: 'AccessFu:MoveByGranularity',
   1.467 +      json: {
   1.468 +        direction: 'Previous',
   1.469 +        granularity: this._granularityMap[aGranularity]
   1.470 +      }
   1.471 +    };
   1.472 +  },
   1.473 +
   1.474 +  moveCaretNextBy: function moveCaretNextBy(aGranularity) {
   1.475 +    return {
   1.476 +      name: 'AccessFu:MoveCaret',
   1.477 +      json: {
   1.478 +        direction: 'Next',
   1.479 +        granularity: this._granularityMap[aGranularity]
   1.480 +      }
   1.481 +    };
   1.482 +  },
   1.483 +
   1.484 +  moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
   1.485 +    return {
   1.486 +      name: 'AccessFu:MoveCaret',
   1.487 +      json: {
   1.488 +        direction: 'Previous',
   1.489 +        granularity: this._granularityMap[aGranularity]
   1.490 +      }
   1.491 +    };
   1.492 +  },
   1.493 +
   1.494 +  _granularityMap: {
   1.495 +    'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
   1.496 +    'word': 2, // MOVEMENT_GRANULARITY_WORD
   1.497 +    'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
   1.498 +  }
   1.499 +};
   1.500 +
   1.501 +var AndroidEvent = {
   1.502 +  VIEW_CLICKED: 0x01,
   1.503 +  VIEW_LONG_CLICKED: 0x02,
   1.504 +  VIEW_SELECTED: 0x04,
   1.505 +  VIEW_FOCUSED: 0x08,
   1.506 +  VIEW_TEXT_CHANGED: 0x10,
   1.507 +  WINDOW_STATE_CHANGED: 0x20,
   1.508 +  VIEW_HOVER_ENTER: 0x80,
   1.509 +  VIEW_HOVER_EXIT: 0x100,
   1.510 +  VIEW_SCROLLED: 0x1000,
   1.511 +  VIEW_TEXT_SELECTION_CHANGED: 0x2000,
   1.512 +  ANNOUNCEMENT: 0x4000,
   1.513 +  VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
   1.514 +  VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
   1.515 +};

mercurial