Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1886)

Unified Diff: chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js

Issue 1302763002: Add tests for braille commands. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@js2gtesterr
Patch Set: Rebase Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js
diff --git a/chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js b/chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js
new file mode 100644
index 0000000000000000000000000000000000000000..2a770344897e1a3f56c2ce68cb6c6e7604104669
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js
@@ -0,0 +1,328 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
dmazzoni 2015/08/24 19:05:07 2015?
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This file contains the |MockFeedback| class which is a
dmazzoni 2015/08/24 19:05:06 Could you document what happens to initial speech
+ * combined mock class for speech and braille feedback. A test that uses
+ * this class may add expectations for speech utterances and braille display
+ * content to be output. The |install| method sets appropriate mock classes
+ * as the |cvox.ChromeVox.tts| and |cvox.ChromeVox.braille| objects,
+ * respectively. Output sent to those objects will then be collected in
+ * an internal queue.
+ *
+ * Expectaations can be added using the |expectSpeech| and |expectBraille|
dmazzoni 2015/08/24 19:05:06 Expectaations -> Expectations
+ * methods. These methods take either strings or regular expressions to match
+ * against. Strings must match a full utterance (or display content) exactly,
+ * while a regular expression must match a substring (use anchor operators if
+ * needed).
+ *
+ * Function calls may be inserted in the stream of expectations using the
+ * |call| method. Such callbacks are called after all preceding expectations
+ * have been met, and before any further expectations are matched. Callbacks
+ * are called in the order they were added to the mock.
+ *
+ * The |replay| method starts processing any pending utterances and braille
+ * display content and will try to match expectations as new feedback enters
+ * the queue asynchronously. When all expectations have been met and callbacks
+ * called, the finish callback, if any was provided to the constructor, is
+ * called. The mock is lean, meaning that feedback that doesn't match
+ * any expectations is silently ignored.
+ *
+ * NOTE: for asynchronous tests, the processing will never finish if there
+ * are unmet expectations. To help debugging in such situations, the mock
+ * will output its pending state if there are pending expectations and no
+ * output is received within a few seconds.
+ *
+ * See mock_feedback_test.js for example usage of this class.
+ */
+
+/**
+ * Combined mock class for braille and speech output.
+ * @param {function=} opt_finishedCallback Called when all expectaions have
dmazzoni 2015/08/24 19:05:06 expectations
+ * been met.
+ * @constructor
+ */
+var MockFeedback = function(opt_finishedCallback) {
+ /**
+ * @type {function}
+ * @private
+ */
+ this.finishedCallback_ = opt_finishedCallback || null;
+ /**
+ * True when |replay| has been called and actions are being replayed.
+ * @type {boolean}
+ * @private
+ */
+ this.replaying_ = false;
+ /**
+ * True when inside the |process| function to prevent nested calls.
+ * @type {boolean}
+ * @private
+ */
+ this.inProcess_ = false;
+ /**
+ * Pending expectations and callbacks.
+ * @type {Array<{perform: function(): boolean, describe: function(): string}>}
+ * @private
+ */
+ this.pendingActions_ = [];
+ /**
+ * Pending speech utterances.
+ * @type {Array<{text: string, callback: (function|undefined)}>}
+ * @private
+ */
+ this.pendingUtterances_ = [];
+ /**
+ * Pending braille output.
+ * @type {Array<{text: string, callback: (function|undefined)}>}
+ * @private
+ */
+ this.pendingBraille_ = [];
+ /**
+ * Handle for the timeout set for debug logging.
+ * @type {number}
+ * @private
+ */
+ this.logTimeoutId_ = 0;
+ /**
+ * @type {cvox.NavBraille}
+ * @private
+ */
+ this.lastMatchedBraille_ = null;
+};
+
+MockFeedback.prototype = {
+
+ /**
+ * Install mock objects as |cvox.CrhomeVox.tts| and |cvox.ChromeVox.braille|
dmazzoni 2015/08/24 19:05:07 ChromeVox
+ * to collect feedback.
+ */
+ install: function() {
+ assertFalse(this.replaying_);
+ function MockTts() {}
dmazzoni 2015/08/24 19:05:07 I know this is legal syntax but it looks funny, li
+ MockTts.prototype = {
+ __proto__: cvox.TtsInterface.prototype,
+ speak: this.addUtterance_.bind(this)
+ };
+ cvox.ChromeVox.tts = new MockTts();
+ function MockBraille() {
+ }
+ MockBraille.prototype = {
+ __proto__: cvox.BrailleInterface.prototype,
+ write: this.addBraille_.bind(this)
+ };
+ cvox.ChromeVox.braille = new MockBraille();
+ },
+
+ /**
+ * Adds an expectation for a spoken utterance.
dmazzoni 2015/08/24 19:05:06 Or utterances?
+ * @param {...string} var_args
dmazzoni 2015/08/24 19:05:06 Document this, i.e. clarify that each argument is
+ * @return {MockFeedback} |this| for chaining
+ */
+ expectSpeech: function() {
+ assertFalse(this.replaying_);
+ Array.prototype.forEach.call(arguments, function(text) {
+ this.pendingActions_.push({
+ perform: function() {
+ return !!MockFeedback.matchAndConsume_(
+ text, {}, this.pendingUtterances_);
+ }.bind(this),
+ describe: function() { return 'Speak \'' + text + '\''; }
dmazzoni 2015/08/24 19:05:06 Could this be toString()?
+ });
+ }.bind(this));
+ return this;
+ },
+
+ /**
+ * Adds an expectation for braille output.
+ * @param {string|RegExp} text
+ * @param {Object=} opt_props Additional properties to match in the
+ * |NavBraille|
+ * @return {MockFeedback} |this| for chaining
+ */
+ expectBraille: function(text, opt_props) {
+ assertFalse(this.replaying_);
+ var props = opt_props || {};
+ this.pendingActions_.push({
+ perform: function() {
+ var match = MockFeedback.matchAndConsume_(
+ text, props, this.pendingBraille_);
+ if (match)
+ this.lastMatchedBraille_ = match;
+ return !!match;
+ }.bind(this),
+ describe: function() {
+ return 'Braille \'' + text + '\' ' + JSON.stringify(props);
+ }
+ });
+ return this;
+ },
+
+ /**
+ * Arranges for a callback to be invoked when all expectations that were
+ * added before this call have been met. Callbacks are called in the
+ * order they are added.
+ * @param {Function} callback
+ * @return {MockFeedback} |this| for chaining
+ */
+ call: function(callback) {
+ assertFalse(this.replaying_);
+ this.pendingActions_.push({
+ perform: function() {
+ callback();
+ return true;
+ },
+ describe: function() {
+ return 'Callback';
+ }
+ });
+ return this;
+ },
+
+ /**
+ * Processes any feedback that has been received so far and treis to
+ * satisfy the registered expectations. Any feedback that is received
+ * after this call (via the installed mock objects) is processed immediately.
+ * When all expectations are satisfied and registered callbacks called,
+ * the finish callbcak, if any, is called.
+ * This function may only be called once.
+ */
+ replay: function() {
dmazzoni 2015/08/24 19:05:06 Do you think it would be possible to raise an exce
+ assertFalse(this.replaying_);
+ this.replaying_ = true;
+ this.process_();
+ },
+
+ /**
+ * Returns the |NavBraille| that matched an expectation. This is
+ * intended to be used by a callback to invoke braille commands that
+ * depend on display contents.
+ * @type {cvox.NavBraille}
+ */
+ get lastMatchedBraille() {
+ assertTrue(this.replaying_);
+ return this.lastMatchedBraille_;
+ },
+
+ /**
+ * @param {string} textSTring
dmazzoni 2015/08/24 19:05:07 textString
+ * @param {cvox.QueueMode} queueMode
+ * @param {Object=} properties
+ * @private
+ */
+ addUtterance_: function(textString, queueMode, properties) {
+ this.pendingUtterances_.push(
+ {text: textString,
+ callback: properties ? properties.endCallback : undefined});
dmazzoni 2015/08/24 19:05:07 Do you need to handle startCallback too? If we wa
+ this.process_();
+ },
+
+ /** @private */
+ addBraille_: function(navBraille) {
+ this.pendingBraille_.push(navBraille);
+ this.process_();
+ },
+
+ /*** @private */
+ process_: function() {
+ if (!this.replaying_ || this.inProcess_)
+ return;
+ try {
+ this.inProcess_ = true;
+ while (this.pendingActions_.length > 0) {
+ var action = this.pendingActions_[0];
+ if (action.perform()) {
+ this.pendingActions_.shift();
+ if (this.logTimeoutId_) {
+ window.clearTimeout(this.logTimeoutId_);
+ this.logTimeoutId_ = 0;
+ }
+ } else {
+ break;
+ }
+ }
+ if (this.pendingActions_.length == 0) {
+ if (this.finishedCallback_) {
+ this.finishedCallback_();
+ this.finishedCallback_ = null;
+ }
+ } else {
+ // If there are pending actions and no matching feedback for a few
+ // seconds, log the pending state to ease debugging.
+ if (!this.logTimeoutId_) {
+ this.logTimeoutId_ = window.setTimeout(
+ this.logPendingState_.bind(this), 2000);
+ }
+ }
+ } finally {
+ this.inProcess_ = false;
+ }
+ },
+
+ /** @private */
+ logPendingState_: function() {
+ if (this.pendingActions_.length > 0)
+ console.log('Still waiting for ' + this.pendingActions_[0].describe());
+ function logPending(desc, list) {
+ if (list.length > 0)
+ console.log('Pending ' + desc + ':\n ' +
+ list.map(function(i) {
+ var ret = '\'' + i.text + '\'';
+ if ('startIndex' in i)
+ ret += ' startIndex=' + i.startIndex;
+ if ('endIndex' in i)
+ ret += ' endIndex=' + i.endIndex;
+ return ret;
+ }).join('\n ') + '\n ');
+ }
+ logPending('speech utterances', this.pendingUtterances_);
+ logPending('braille', this.pendingBraille_);
+ this.logTimeoutId_ = 0;
+ },
+};
+
+/**
+ * @param {string} text
+ * @param {Object} props
+ * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>}
+ * pending
+ * @return {Object}
+ * @private
+ */
+MockFeedback.matchAndConsume_ = function(text, props, pending) {
+ for (var i = 0, candidate; candidate = pending[i]; ++i) {
dmazzoni 2015/08/24 19:05:07 This seems to allow additional utterances - for ex
+ var candidateText = candidate.text.toString();
+ if (text === candidateText ||
+ (text instanceof RegExp && text.test(candidateText))) {
+ var matched = true;
+ for (prop in props) {
+ if (candidate[prop] !== props[prop]) {
+ matched = false;
+ break;
+ }
+ }
+ if (matched)
+ break;
+ }
+ }
+ if (candidate) {
+ var consumed = pending.splice(0, i + 1);
+ consumed.forEach(function(item) {
+ if (item.callback)
+ item.callback();
+ });
+ }
+ return candidate;
+};
+
+/**
+ * Returns its first argumennt. Moved out of the cosntructor to satisfy the
dmazzoni 2015/08/24 19:05:06 constructor
+ * js linter.
+ * @param {*} a
+ * @return {*}
+ * @private
+ */
+MockFeedback.identityFunction_ = function(a) {
+ return a;
+};

Powered by Google App Engine
This is Rietveld 408576698