| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview This file contains the |MockFeedback| class which is a | 6 * @fileoverview This file contains the |MockFeedback| class which is |
| 7 * combined mock class for speech and braille feedback. A test that uses | 7 * a combined mock class for speech, braille, and earcon feedback. A |
| 8 * this class may add expectations for speech utterances and braille display | 8 * test that uses this class may add expectations for speech |
| 9 * content to be output. The |install| method sets appropriate mock classes | 9 * utterances, braille display content to be output, and earcons |
| 10 * as the |cvox.ChromeVox.tts| and |cvox.ChromeVox.braille| objects, | 10 * played (by name). The |install| method sets appropriate mock |
| 11 * respectively. Output sent to those objects will then be collected in | 11 * classes as the |cvox.ChromeVox.tts|, |cvox.ChromeVox.braille| and |
| 12 * an internal queue. | 12 * |cvox.ChromeVox.earcons| objects, respectively. Output sent to |
| 13 * those objects will then be collected in an internal queue. |
| 13 * | 14 * |
| 14 * Expectations can be added using the |expectSpeech| and |expectBraille| | 15 * Expectations can be added using the |expectSpeech|, |
| 15 * methods. These methods take either strings or regular expressions to match | 16 * |expectBraille|, and |expectEarcon| methods. These methods take |
| 16 * against. Strings must match a full utterance (or display content) exactly, | 17 * either strings or regular expressions to match against. Strings |
| 17 * while a regular expression must match a substring (use anchor operators if | 18 * must match a full utterance (or display content) exactly, while a |
| 19 * regular expression must match a substring (use anchor operators if |
| 18 * needed). | 20 * needed). |
| 19 * | 21 * |
| 20 * Function calls may be inserted in the stream of expectations using the | 22 * Function calls may be inserted in the stream of expectations using the |
| 21 * |call| method. Such callbacks are called after all preceding expectations | 23 * |call| method. Such callbacks are called after all preceding expectations |
| 22 * have been met, and before any further expectations are matched. Callbacks | 24 * have been met, and before any further expectations are matched. Callbacks |
| 23 * are called in the order they were added to the mock. | 25 * are called in the order they were added to the mock. |
| 24 * | 26 * |
| 25 * The |replay| method starts processing any pending utterances and braille | 27 * The |replay| method starts processing any pending utterances, |
| 26 * display content and will try to match expectations as new feedback enters | 28 * braille display content, and earcons and will try to match |
| 27 * the queue asynchronously. When all expectations have been met and callbacks | 29 * expectations as new feedback enters the queue asynchronously. When |
| 28 * called, the finish callback, if any was provided to the constructor, is | 30 * all expectations have been met and callbacks called, the finish |
| 29 * called. | 31 * callback, if any was provided to the constructor, is called. |
| 30 * | 32 * |
| 31 * This mock class is lean, meaning that feedback that doesn't match | 33 * This mock class is lean, meaning that feedback that doesn't match |
| 32 * any expectations is silently ignored. | 34 * any expectations is silently ignored. |
| 33 * | 35 * |
| 34 * NOTE: for asynchronous tests, the processing will never finish if there | 36 * NOTE: for asynchronous tests, the processing will never finish if there |
| 35 * are unmet expectations. To help debugging in such situations, the mock | 37 * are unmet expectations. To help debugging in such situations, the mock |
| 36 * will output its pending state if there are pending expectations and no | 38 * will output its pending state if there are pending expectations and no |
| 37 * output is received within a few seconds. | 39 * output is received within a few seconds. |
| 38 * | 40 * |
| 39 * See mock_feedback_test.js for example usage of this class. | 41 * See mock_feedback_test.js for example usage of this class. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 * @private | 77 * @private |
| 76 */ | 78 */ |
| 77 this.pendingUtterances_ = []; | 79 this.pendingUtterances_ = []; |
| 78 /** | 80 /** |
| 79 * Pending braille output. | 81 * Pending braille output. |
| 80 * @type {Array<{text: string, callback: (function|undefined)}>} | 82 * @type {Array<{text: string, callback: (function|undefined)}>} |
| 81 * @private | 83 * @private |
| 82 */ | 84 */ |
| 83 this.pendingBraille_ = []; | 85 this.pendingBraille_ = []; |
| 84 /** | 86 /** |
| 87 * Pending earcons. |
| 88 * @type {Array<{text: string, callback: (function|undefined)}>} |
| 89 * @private |
| 90 */ |
| 91 this.pendingEarcons_ = []; |
| 92 /** |
| 85 * Handle for the timeout set for debug logging. | 93 * Handle for the timeout set for debug logging. |
| 86 * @type {number} | 94 * @type {number} |
| 87 * @private | 95 * @private |
| 88 */ | 96 */ |
| 89 this.logTimeoutId_ = 0; | 97 this.logTimeoutId_ = 0; |
| 90 /** | 98 /** |
| 91 * @type {cvox.NavBraille} | 99 * @type {cvox.NavBraille} |
| 92 * @private | 100 * @private |
| 93 */ | 101 */ |
| 94 this.lastMatchedBraille_ = null; | 102 this.lastMatchedBraille_ = null; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 111 | 119 |
| 112 cvox.ChromeVox.tts = new MockTts(); | 120 cvox.ChromeVox.tts = new MockTts(); |
| 113 | 121 |
| 114 var MockBraille = function() {}; | 122 var MockBraille = function() {}; |
| 115 MockBraille.prototype = { | 123 MockBraille.prototype = { |
| 116 __proto__: cvox.BrailleInterface.prototype, | 124 __proto__: cvox.BrailleInterface.prototype, |
| 117 write: this.addBraille_.bind(this) | 125 write: this.addBraille_.bind(this) |
| 118 }; | 126 }; |
| 119 | 127 |
| 120 cvox.ChromeVox.braille = new MockBraille(); | 128 cvox.ChromeVox.braille = new MockBraille(); |
| 129 |
| 130 var MockEarcons = function() {}; |
| 131 MockEarcons.prototype = { |
| 132 __proto__: cvox.AbstractEarcons.prototype, |
| 133 playEarcon: this.addEarcon_.bind(this) |
| 134 }; |
| 135 |
| 136 // cvox.ChromeVox.earcons is a getter that switches between Classic and |
| 137 // Next; replace it with MockEarcons. |
| 138 delete cvox.ChromeVox.earcons; |
| 139 cvox.ChromeVox.earcons = new MockEarcons(); |
| 121 }, | 140 }, |
| 122 | 141 |
| 123 /** | 142 /** |
| 124 * Adds an expectation for one or more spoken utterances. | 143 * Adds an expectation for one or more spoken utterances. |
| 125 * @param {...(string|RegExp)} var_args One or more utterance to add as | 144 * @param {...(string|RegExp)} var_args One or more utterance to add as |
| 126 * expectations. | 145 * expectations. |
| 127 * @return {MockFeedback} |this| for chaining | 146 * @return {MockFeedback} |this| for chaining |
| 128 */ | 147 */ |
| 129 expectSpeech: function() { | 148 expectSpeech: function() { |
| 130 assertFalse(this.replaying_); | 149 assertFalse(this.replaying_); |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 return !!match; | 209 return !!match; |
| 191 }.bind(this), | 210 }.bind(this), |
| 192 toString: function() { | 211 toString: function() { |
| 193 return 'Braille \'' + text + '\' ' + JSON.stringify(props); | 212 return 'Braille \'' + text + '\' ' + JSON.stringify(props); |
| 194 } | 213 } |
| 195 }); | 214 }); |
| 196 return this; | 215 return this; |
| 197 }, | 216 }, |
| 198 | 217 |
| 199 /** | 218 /** |
| 219 * Adds an expectation for a played earcon. |
| 220 * @param {string} earconName The name of the earcon. |
| 221 * @return {MockFeedback} |this| for chaining |
| 222 */ |
| 223 expectEarcon: function(earconName, opt_props) { |
| 224 assertFalse(this.replaying_); |
| 225 this.pendingActions_.push({ |
| 226 perform: function() { |
| 227 var match = MockFeedback.matchAndConsume_( |
| 228 earconName, {}, this.pendingEarcons_); |
| 229 return !!match; |
| 230 }.bind(this), |
| 231 toString: function() { |
| 232 return 'Earcon \'' + earconName + '\''; |
| 233 } |
| 234 }); |
| 235 return this; |
| 236 }, |
| 237 |
| 238 /** |
| 200 * Arranges for a callback to be invoked when all expectations that were | 239 * Arranges for a callback to be invoked when all expectations that were |
| 201 * added before this call have been met. Callbacks are called in the | 240 * added before this call have been met. Callbacks are called in the |
| 202 * order they are added. | 241 * order they are added. |
| 203 * @param {Function} callback | 242 * @param {Function} callback |
| 204 * @return {MockFeedback} |this| for chaining | 243 * @return {MockFeedback} |this| for chaining |
| 205 */ | 244 */ |
| 206 call: function(callback) { | 245 call: function(callback) { |
| 207 assertFalse(this.replaying_); | 246 assertFalse(this.replaying_); |
| 208 this.pendingActions_.push({ | 247 this.pendingActions_.push({ |
| 209 perform: function() { | 248 perform: function() { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 callback: callback}); | 302 callback: callback}); |
| 264 this.process_(); | 303 this.process_(); |
| 265 }, | 304 }, |
| 266 | 305 |
| 267 /** @private */ | 306 /** @private */ |
| 268 addBraille_: function(navBraille) { | 307 addBraille_: function(navBraille) { |
| 269 this.pendingBraille_.push(navBraille); | 308 this.pendingBraille_.push(navBraille); |
| 270 this.process_(); | 309 this.process_(); |
| 271 }, | 310 }, |
| 272 | 311 |
| 312 /** @private */ |
| 313 addEarcon_: function(earconName) { |
| 314 this.pendingEarcons_.push({text: earconName}); |
| 315 this.process_(); |
| 316 }, |
| 317 |
| 273 /*** @private */ | 318 /*** @private */ |
| 274 process_: function() { | 319 process_: function() { |
| 275 if (!this.replaying_ || this.inProcess_) | 320 if (!this.replaying_ || this.inProcess_) |
| 276 return; | 321 return; |
| 277 try { | 322 try { |
| 278 this.inProcess_ = true; | 323 this.inProcess_ = true; |
| 279 while (this.pendingActions_.length > 0) { | 324 while (this.pendingActions_.length > 0) { |
| 280 var action = this.pendingActions_[0]; | 325 var action = this.pendingActions_[0]; |
| 281 if (action.perform()) { | 326 if (action.perform()) { |
| 282 this.pendingActions_.shift(); | 327 this.pendingActions_.shift(); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 var ret = '\'' + i.text + '\''; | 362 var ret = '\'' + i.text + '\''; |
| 318 if ('startIndex' in i) | 363 if ('startIndex' in i) |
| 319 ret += ' startIndex=' + i.startIndex; | 364 ret += ' startIndex=' + i.startIndex; |
| 320 if ('endIndex' in i) | 365 if ('endIndex' in i) |
| 321 ret += ' endIndex=' + i.endIndex; | 366 ret += ' endIndex=' + i.endIndex; |
| 322 return ret; | 367 return ret; |
| 323 }).join('\n ') + '\n '); | 368 }).join('\n ') + '\n '); |
| 324 } | 369 } |
| 325 logPending('speech utterances', this.pendingUtterances_); | 370 logPending('speech utterances', this.pendingUtterances_); |
| 326 logPending('braille', this.pendingBraille_); | 371 logPending('braille', this.pendingBraille_); |
| 372 logPending('earcons', this.pendingEarcons_); |
| 327 this.logTimeoutId_ = 0; | 373 this.logTimeoutId_ = 0; |
| 328 }, | 374 }, |
| 329 }; | 375 }; |
| 330 | 376 |
| 331 /** | 377 /** |
| 332 * @param {string} text | 378 * @param {string} text |
| 333 * @param {Object} props | 379 * @param {Object} props |
| 334 * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>} | 380 * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>} |
| 335 * pending | 381 * pending |
| 336 * @return {Object} | 382 * @return {Object} |
| 337 * @private | 383 * @private |
| 338 */ | 384 */ |
| 339 MockFeedback.matchAndConsume_ = function(text, props, pending) { | 385 MockFeedback.matchAndConsume_ = function(text, props, pending) { |
| 340 for (var i = 0, candidate; candidate = pending[i]; ++i) { | 386 for (var i = 0, candidate; candidate = pending[i]; ++i) { |
| 341 var candidateText = candidate.text.toString(); | 387 var candidateText = candidate.text; |
| 388 if (typeof(candidateText) != 'string') |
| 389 candidateText = candidateText.toString(); |
| 390 |
| 342 if (text === candidateText || | 391 if (text === candidateText || |
| 343 (text instanceof RegExp && text.test(candidateText))) { | 392 (text instanceof RegExp && text.test(candidateText))) { |
| 344 var matched = true; | 393 var matched = true; |
| 345 for (prop in props) { | 394 for (prop in props) { |
| 346 if (candidate[prop] !== props[prop]) { | 395 if (candidate[prop] !== props[prop]) { |
| 347 matched = false; | 396 matched = false; |
| 348 break; | 397 break; |
| 349 } | 398 } |
| 350 } | 399 } |
| 351 if (matched) | 400 if (matched) |
| 352 break; | 401 break; |
| 353 } | 402 } |
| 354 } | 403 } |
| 355 if (candidate) { | 404 if (candidate) { |
| 356 var consumed = pending.splice(0, i + 1); | 405 var consumed = pending.splice(0, i + 1); |
| 357 consumed.forEach(function(item) { | 406 consumed.forEach(function(item) { |
| 358 if (item.callback) | 407 if (item.callback) |
| 359 item.callback(); | 408 item.callback(); |
| 360 }); | 409 }); |
| 361 } | 410 } |
| 362 return candidate; | 411 return candidate; |
| 363 }; | 412 }; |
| OLD | NEW |