Roll leveldb 3f7758:803d69 (v1.17 -> v1.18)
[chromium-blink-merge.git] / chrome / test / data / chromeos / virtual_keyboard / virtual_keyboard_test_base.js
bloba46633fe7a0587f0de60ce1f28a0da5e5980925f
1 /*
2  * Copyright 2013 The Chromium Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
7 /**
8  * Set to true while debugging virtual keyboard tests, for verbose debug output.
9  */
10 var debugging = false;
12 /**
13  * The enumeration of keyset modifiers.
14  * @enum {string}
15  */
16 var KeysetModifier = {
17   NONE: 'none',
18   SHIFT: 'shift',
19   MORE: 'more',
20   SYMBOL: 'symbol'
23 /**
24  * Flag values for the shift, control and alt modifiers as defined by
25  * EventFlags in "event_constants.h".
26  * @type {enum}
27  */
28 var Modifier = {
29   NONE: 0,
30   SHIFT: 2,
31   CONTROL: 4,
32   ALT: 8
35 /**
36  * Display diagnostic messages when debugging tests.
37  * @param {string} Message to conditionally display.
38  */
39 function Debug(message) {
40   if (debugging)
41     console.log(message);
44 var mockController;
46 /**
47  * Adds mocks for chrome extension API calls.
48  *
49  * @param {MockController} mockController Controller for validating
50  *     calls with expectations.
51  */
52 function mockExtensionApis(mockController) {
54   /**
55    * Mocks methods within a namespace.
56    * @param {string} namespace Dot delimited namespace.
57    * @param {Array.<string>} methods List of methods names to mock.
58    */
59   var addMocks = function(namespace, methods) {
60     var parts = namespace.split('.');
61     var base = window;
62     for (var i = 0; i < parts.length; i++) {
63       if (!base[parts[i]])
64         base[parts[i]] = {};
65       base = base[parts[i]];
66     }
67     methods.forEach(function(m) {
68       var fn = base[m] = mockController.createFunctionMock(m);
69       fn.functionName = [namespace, m].join('.');
70       // Method to arm triggering a callback function with the specified
71       // arguments. Skip validation of callbacks.
72       fn.setCallbackData = function() {
73         fn.callbackData = Array.prototype.slice.call(arguments);
74         fn.verifyMock = function() {};
75       };
76       // TODO(kevers): Add validateCall override that strips functions from the
77       // argument signature before calling MockMethod.Prototype.validateCall
78     });
79   };
81   var virtualKeyboardPrivateMethods = [
82     'getKeyboardConfig',
83     'hideKeyboard',
84     'insertText',
85     'lockKeyboard',
86     'moveCursor',
87     'sendKeyEvent'
88   ];
90   var inputMethodPrivateMethods = [
91     'getCurrentInputMethod',
92     'getInputMethods',
93     'setCurrentInputMethod'
94   ];
96   addMocks('chrome.virtualKeyboardPrivate', virtualKeyboardPrivateMethods);
97   addMocks('chrome.inputMethodPrivate', inputMethodPrivateMethods);
98   addMocks('chrome.runtime', ['getBackgroundPage']);
99   addMocks('chrome.runtime.onMessage', ['addListener']);
100   // Ignore calls to addListener. Reevaluate if important to properly track the
101   // flow of events.
102   chrome.runtime.onMessage.addListener = function() {};
104   chrome.i18n = {};
105   chrome.i18n.getMessage = function(name) {
106     return name;
107   };
111  * Create mocks for the virtualKeyboardPrivate API. Any tests that trigger API
112  * calls must set expectations for call signatures.
113  */
114 function setUp() {
115   mockController = new MockController();
117   // It is not safe to install the mockTimer during setUp, as intialization of
118   // the keyboard uses polling to determine when loading is complete. Instead,
119   // install the mockTimer as needed once a test is initiated and unintall on
120   // completion of the test.
122   mockExtensionApis(mockController);
124   var validateSendCall = function(index, expected, observed) {
125     // Only consider the first argument (VirtualKeyEvent) for the validation of
126     // sendKeyEvent calls.
127     var expectedEvent = expected[0];
128     var observedEvent = observed[0];
129     assertEquals(expectedEvent.type,
130                  observedEvent.type,
131                  'Mismatched event types.');
132     assertEquals(expectedEvent.charValue,
133                  observedEvent.charValue,
134                  'Mismatched unicode values for character.');
135     assertEquals(expectedEvent.keyCode,
136                  observedEvent.keyCode,
137                  'Mismatched key codes.');
138     assertEquals(expectedEvent.modifiers,
139                  observedEvent.modifiers,
140                  'Mismatched states for modifiers.');
141   };
142   chrome.virtualKeyboardPrivate.sendKeyEvent.validateCall = validateSendCall;
144   var validateLockKeyboard = function(index, expected, observed) {
145     assertEquals(expected[0],
146                  observed[0],
147                  'Mismatched keyboard lock/unlock state.');
148   };
149   chrome.virtualKeyboardPrivate.lockKeyboard.validateCall =
150       validateLockKeyboard;
152   chrome.virtualKeyboardPrivate.hideKeyboard.validateCall = function() {
153     // hideKeyboard has one optional argument for error logging that does not
154     // matter for the purpose of validating the call.
155   };
157   // Set data to be provided to callbacks in response to API calls.
158   // TODO(kevers): Provide mechanism to override these values for individual
159   // tests as needed.
160   chrome.virtualKeyboardPrivate.getKeyboardConfig.setCallbackData({
161     layout: 'qwerty',
162     a11ymode: false,
163     experimental: false
164   });
166   chrome.inputMethodPrivate.getCurrentInputMethod.setCallbackData('us:en');
168   chrome.inputMethodPrivate.getInputMethods.setCallbackData([
169     {id: 'us', name: 'US Keyboard', indicator: 'US'},
170     {id: 'fr', name: 'French Keyboard', indicator: 'Fr'},
171     {id: 'de', name: 'German Keyboard', indicator: 'De'}
172   ]);
174   chrome.runtime.getBackgroundPage.setCallbackData(undefined);
176   // TODO(kevers): Mock additional extension API calls as required.
180  * Verify that API calls match expectations.
181  */
182 function tearDown() {
183   mockController.verifyMocks();
184   mockController.reset();
188 // ------------------- Helper Functions -------------------------
192  * Runs a test asynchronously once keyboard loading is complete.
193  * @param {Function} runTestCallback Asynchronous test function.
194  * @param {Object=} opt_config Optional configuration for the keyboard.
195  */
196 function onKeyboardReady(runTestCallback, opt_config) {
197   var default_config = {
198     keyset: 'us.compact.qwerty',
199     languageCode: 'en',
200     passwordLayout: 'us',
201     name: 'English'
202   };
203   var config = opt_config || default_config;
204   chrome.virtualKeyboardPrivate.keyboardLoaded = function() {
205     // Disarm to prevent repeated calls to run a test.
206     chrome.virtualKeyboardPrivate.keyboardLoaded = function() {};
207     runTestCallback();
208   };
209   window.initializeVirtualKeyboard(config.keyset,
210                                    config.languageCode,
211                                    config.passwordLayout,
212                                    config.name);
217  * Mocks a touch event targeted on a key.
218  * @param {!Element} key .
219  * @param {string} eventType .
220  */
221 function mockTouchEvent(key, eventType) {
222   var e = new Event(eventType, {
223     bubbles: true,
224     cancelable: true
225   });
226   return key.dispatchEvent(e);
230  * Simulates tapping on a key.
231  * @param {!Element} key .
232  */
233 function mockTap(key) {
234   mockTouchEvent(key, 'touchstart');
235   mockTouchEvent(key, 'touchend');
239  * Returns the active keyboard view.
240  * @return {!HTMLElement}
241  */
242 function getActiveView() {
243   var container = document.querySelector('.inputview-container');
244   var views = container.querySelectorAll('.inputview-view');
245   for (var i = 0; i < views.length; i++) {
246     var display = views[i].style.display;
247     if (!display || display != 'none') {
248       return views[i];
249     }
250   }
254  * Finds the ancestor element corresponding the the soft key view.
255  * @param {Element} source .
256  * @return {?Element} .
257  */
258 function getSoftKeyView(source) {
259   var parent = source.parentElement;
260   while (parent && !parent.classList.contains('inputview-skv')) {
261     parent = parent.parentElement;
262   }
263   return parent;
267  * Locates a key by label.
268  * @param {string} label The label on the key.  If the key has multiple labels,
269  *    |label| can match any of them.
270  * @param {string=} opt_rowId Optional ID of the row containing the key.
271  * @returns {?Element} .
272  */
273 function findKey(label, opt_rowId) {
274   var view = getActiveView();
275   assertTrue(!!view, 'Unable to find active keyboard view');
276   if (opt_rowId) {
277     view = view.querySelector('#' + opt_rowId);
278     assertTrue(!!view, 'Unable to locate row ' + opt_rowId);
279   }
280   var candidates = view.querySelectorAll('.inputview-ch');
281   // Compact layouts use a different naming convention.
282   if (candidates.length == 0)
283     candidates = view.querySelectorAll('.inputview-special-key-name');
284   for (var i = 0; i < candidates.length; i++) {
285     if (candidates[i].textContent == label)
286       return getSoftKeyView(candidates[i]);
287   }
288   assertTrue(false, 'Cannot find key labeled \'' + label + '\'');
292  * Locates a key with matching ID. Note that IDs are not necessarily unique
293  * across keysets; however, it is unique within the active layout.
294  */
295 function findKeyById(label) {
296   var view = getActiveView();
297   assertTrue(!!view, 'Unable to find active keyboard view');
298   var key = view.querySelector('#' + label);
299   assertTrue(!!key, 'Cannot find key with ID ' + label);
300   return key;
304  * Verifies if a key contains a matching label.
305  * @param {Element} key .
306  * @param {string} label .
307  * @return {boolean} .
308  */
309 function hasLabel(key, label) {
310   var characters = key.querySelectorAll('.inputview-ch');
311   // Compact layouts represent keys differently.
312   if (characters.length == 0)
313     characters = key.querySelectorAll('.inputview-special-key-name');
314   for (var i = 0; i < characters.length; i++) {
315     if (characters[i].textContent == label)
316       return true;
317   }
318   return false;
322  * Mock typing of basic keys. Each keystroke should trigger a pair of
323  * API calls to send viritual key events.
324  * @param {string} label The character being typed.
325  * @param {number} keyCode The legacy key code for the character.
326  * @param {number} modifiers Indicates which if any of the shift, control and
327  *     alt keys are being virtually pressed.
328  * @param {number=} opt_unicode Optional unicode value for the character. Only
329  *     required if it cannot be directly calculated from the label.
330  */
331 function mockTypeCharacter(label, keyCode, modifiers, opt_unicode) {
332   var key = label.length == 1 ? findKey(label) : findKeyById(label);
333   var unicodeValue = opt_unicode || label.charCodeAt(0);
334   var send = chrome.virtualKeyboardPrivate.sendKeyEvent;
335   send.addExpectation({
336     type: 'keydown',
337     charValue: unicodeValue,
338     keyCode: keyCode,
339     modifiers: modifiers
340   });
341   send.addExpectation({
342     type: 'keyup',
343     charValue: unicodeValue,
344     keyCode: keyCode,
345     modifiers: modifiers
346   });
347   mockTap(key);
351  * Emulates a user triggering a keyset modifier.
352  * @param {!KeysetModifier} The modifier to apply.
353  */
354 function applyKeysetModifier(modifier) {
355   if (modifier == KeysetModifier.NONE) {
356     return;
357   }
358   var activeView = getActiveView();
359   if (modifier == KeysetModifier.SHIFT) {
360     // Set state of shift key.
361     var leftShift = activeView.querySelector('#ShiftLeft');
362     assertTrue(!!leftShift, 'Unable to find left shift key');
363     var currentShiftState = !!leftShift.querySelector(
364         '.inputview-special-key-highlight');
365     if (!currentShiftState) {
366       mockTap(leftShift);
367     }
368   } else if (modifier == KeysetModifier.SYMBOL) {
369     var switchKey = findKey('?123', 'spaceKeyrow');
370     assertTrue(!!switchKey, 'Unable to find symbol transition key');
371     // Switch to symbol keyset.
372     mockTap(switchKey);
373   } else {
374     var switchKey = findKey('~[<', 'row3');
375     assertTrue(!!switchKey, 'Unable to find more transition key');
376     // Switch to more keyset.
377     mockTap(switchKey);
378   }