Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / chrome / test / data / chromeos / virtual_keyboard / virtual_keyboard_test_base.js
blob12217339af171e1d2438f07f03b09f558d4cdd34
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     'setMode'
89   ];
91   var inputMethodPrivateMethods = [
92     'getCurrentInputMethod',
93     'getInputMethods',
94     'setCurrentInputMethod'
95   ];
97   addMocks('chrome.virtualKeyboardPrivate', virtualKeyboardPrivateMethods);
98   addMocks('chrome.inputMethodPrivate', inputMethodPrivateMethods);
99   addMocks('chrome.runtime', ['getBackgroundPage']);
100   addMocks('chrome.runtime.onMessage', ['addListener']);
101   // Ignore calls to addListener. Reevaluate if important to properly track the
102   // flow of events.
103   chrome.runtime.onMessage.addListener = function() {};
105   chrome.i18n = {};
106   chrome.i18n.getMessage = function(name) {
107     return name;
108   };
112  * Adjust the size and position of the keyboard for testing.
113  */
114 function adjustScreenForTesting() {
115   var style = document.body.style;
116   style.left = 0;
117   style.top = 0;
118   style.bottom = 0;
119   style.right = 0;
120   style.background = 'transparent';
121   style.position = 'absolute';
123   // Adjust positioning for testing in a non-fullscreen display.
124   Object.defineProperty(window.screen, 'width', {
125     get: function() {
126       return document.body.clientWidth;
127     },
128     configurable: true
129   });
130   Object.defineProperty(window.screen, 'height', {
131     get: function() {
132       return document.body.clientHeight;
133     },
134     configurable: true
135   });
139  * Create mocks for the virtualKeyboardPrivate API. Any tests that trigger API
140  * calls must set expectations for call signatures.
141  */
142 function setUp() {
143   mockController = new MockController();
145   // It is not safe to install the mockTimer during setUp, as intialization of
146   // the keyboard uses polling to determine when loading is complete. Instead,
147   // install the mockTimer as needed once a test is initiated and unintall on
148   // completion of the test.
150   mockExtensionApis(mockController);
152   adjustScreenForTesting();
154   var validateSendCall = function(index, expected, observed) {
155     // Only consider the first argument (VirtualKeyEvent) for the validation of
156     // sendKeyEvent calls.
157     var expectedEvent = expected[0];
158     var observedEvent = observed[0];
159     assertEquals(expectedEvent.type,
160                  observedEvent.type,
161                  'Mismatched event types.');
162     assertEquals(expectedEvent.charValue,
163                  observedEvent.charValue,
164                  'Mismatched unicode values for character.');
165     assertEquals(expectedEvent.keyCode,
166                  observedEvent.keyCode,
167                  'Mismatched key codes.');
168     assertEquals(expectedEvent.modifiers,
169                  observedEvent.modifiers,
170                  'Mismatched states for modifiers.');
171   };
172   chrome.virtualKeyboardPrivate.sendKeyEvent.validateCall = validateSendCall;
174   var validateLockKeyboard = function(index, expected, observed) {
175     assertEquals(expected[0],
176                  observed[0],
177                  'Mismatched keyboard lock/unlock state.');
178   };
179   chrome.virtualKeyboardPrivate.lockKeyboard.validateCall =
180       validateLockKeyboard;
182   chrome.virtualKeyboardPrivate.hideKeyboard.validateCall = function() {
183     // hideKeyboard has one optional argument for error logging that does not
184     // matter for the purpose of validating the call.
185   };
187   // Set data to be provided to callbacks in response to API calls.
188   // TODO(kevers): Provide mechanism to override these values for individual
189   // tests as needed.
190   chrome.virtualKeyboardPrivate.getKeyboardConfig.setCallbackData({
191     layout: 'qwerty',
192     a11ymode: false,
193     experimental: false,
194     features: [],
195   });
197   chrome.inputMethodPrivate.getCurrentInputMethod.setCallbackData('us:en');
199   // Set an empty list. Tests that care about input methods in the menu will
200   // need to call this again with their own list of input methods.
201   chrome.inputMethodPrivate.getInputMethods.setCallbackData([]);
203   chrome.runtime.getBackgroundPage.setCallbackData(undefined);
205   // TODO(kevers): Mock additional extension API calls as required.
209  * Verify that API calls match expectations.
210  */
211 function tearDown() {
212   mockController.verifyMocks();
213   mockController.reset();
217 // ------------------- Helper Functions -------------------------
220  * Runs a test asynchronously once keyboard loading is complete.
221  * @param {Function} runTestCallback Asynchronous test function.
222  * @param {Object=} opt_config Optional configuration for the keyboard.
223  */
224 function onKeyboardReady(runTestCallback, opt_config) {
225   var default_config = {
226     keyset: 'us.compact.qwerty',
227     languageCode: 'en',
228     passwordLayout: 'us',
229     name: 'English'
230   };
231   var config = opt_config || default_config;
232   var options = config.options || {};
233   chrome.virtualKeyboardPrivate.keyboardLoaded = function() {
234     runTestCallback();
235   };
236   window.initializeVirtualKeyboard(config.keyset,
237                                    config.languageCode,
238                                    config.passwordLayout,
239                                    config.name,
240                                    options);
244  * Defers continuation of a test until one or more keysets are loaded.
245  * @param {string|Array.<string>} keyset Name of the target keyset or list of
246  *     keysets.
247  * @param {Function} continueTestCallback Callback function to invoke in order
248  *     to resume the test.
249  */
250 function onKeysetsReady(keyset, continueTestCallback) {
251   if (keyset instanceof Array) {
252     var blocked = false;
253     keyset.forEach(function(id) {
254       if (!(id in controller.container_.keysetViewMap))
255         blocked = true;
256     });
257     if (!blocked) {
258       continueTestCallback();
259       return;
260     }
261   } else if (keyset in controller.container_.keysetViewMap) {
262     continueTestCallback();
263     return;
264   }
265   setTimeout(function() {
266     onKeysetsReady(keyset, continueTestCallback);
267   }, 100);
271  * Mocks a touch event targeted on a key.
272  * @param {!Element} key .
273  * @param {string} eventType .
274  */
275 function mockTouchEvent(key, eventType) {
276   var rect = key.getBoundingClientRect();
277   var x = rect.left + rect.width/2;
278   var y = rect.top + rect.height/2;
279   var e = document.createEvent('UIEvent');
280   e.initUIEvent(eventType, true, true);
281   e.touches = [{pageX: x, pageY: y}];
282   e.target = key;
283   return key.dispatchEvent(e);
287  * Simulates tapping on a key.
288  * @param {!Element} key .
289  */
290 function mockTap(key) {
291   mockTouchEvent(key, 'touchstart');
292   mockTouchEvent(key, 'touchend');
296  * Returns the active keyboard view.
297  * @return {!HTMLElement}
298  */
299 function getActiveView() {
300   var container = document.querySelector('.inputview-container');
301   var views = container.querySelectorAll('.inputview-view');
302   for (var i = 0; i < views.length; i++) {
303     var display = views[i].style.display;
304     if (!display || display != 'none') {
305       return views[i];
306     }
307   }
311  * Finds the ancestor element corresponding the the soft key view.
312  * @param {Element} source .
313  * @return {?Element} .
314  */
315 function getSoftKeyView(source) {
316   var parent = source.parentElement;
317   while (parent && !parent.classList.contains('inputview-skv')) {
318     parent = parent.parentElement;
319   }
320   return parent;
324  * Locates a key by label.
325  * @param {string} label The label on the key.  If the key has multiple labels,
326  *    |label| can match any of them.
327  * @param {string=} opt_rowId Optional ID of the row containing the key.
328  * @returns {?Element} .
329  */
330 function findKey(label, opt_rowId) {
331   var view = getActiveView();
332   assertTrue(!!view, 'Unable to find active keyboard view');
333   if (opt_rowId) {
334     view = view.querySelector('#' + opt_rowId);
335     assertTrue(!!view, 'Unable to locate row ' + opt_rowId);
336   }
337   var candidates = view.querySelectorAll('.inputview-ch');
338   // Compact layouts use a different naming convention.
339   if (candidates.length == 0)
340     candidates = view.querySelectorAll('.inputview-special-key-name');
341   for (var i = 0; i < candidates.length; i++) {
342     if (candidates[i].textContent == label)
343       return candidates[i];
344   }
345   assertTrue(false, 'Cannot find key labeled \'' + label + '\'');
349  * Locates a key with matching ID. Note that IDs are not necessarily unique
350  * across keysets; however, it is unique within the active layout.
351  */
352 function findKeyById(label) {
353   var view = getActiveView();
354   assertTrue(!!view, 'Unable to find active keyboard view');
355   var key = view.querySelector('#' + label);
356   assertTrue(!!key, 'Cannot find key with ID ' + label);
357   return key;
361  * Verifies if a key contains a matching label.
362  * @param {Element} key .
363  * @param {string} label .
364  * @return {boolean} .
365  */
366 function hasLabel(key, label) {
367   var characters = key.querySelectorAll('.inputview-ch');
368   // Compact layouts represent keys differently.
369   if (characters.length == 0)
370     characters = key.querySelectorAll('.inputview-special-key-name');
371   for (var i = 0; i < characters.length; i++) {
372     if (characters[i].textContent == label)
373       return true;
374   }
375   return false;
379  * Mock typing of basic keys. Each keystroke should trigger a pair of
380  * API calls to send viritual key events.
381  * @param {string} label The character being typed.
382  * @param {number} keyCode The legacy key code for the character.
383  * @param {number} modifiers Indicates which if any of the shift, control and
384  *     alt keys are being virtually pressed.
385  * @param {number=} opt_unicode Optional unicode value for the character. Only
386  *     required if it cannot be directly calculated from the label.
387  */
388 function mockTypeCharacter(label, keyCode, modifiers, opt_unicode) {
389   var key = label.length == 1 ? findKey(label) : findKeyById(label);
390   // opt_unicode is allowed to be 0.
391   var unicodeValue = opt_unicode;
392   if (unicodeValue === undefined)
393     unicodeValue = label.charCodeAt(0);
394   var send = chrome.virtualKeyboardPrivate.sendKeyEvent;
395   send.addExpectation({
396     type: 'keydown',
397     charValue: unicodeValue,
398     keyCode: keyCode,
399     modifiers: modifiers
400   });
401   send.addExpectation({
402     type: 'keyup',
403     charValue: unicodeValue,
404     keyCode: keyCode,
405     modifiers: modifiers
406   });
407   mockTap(key);
411  * Emulates a user triggering a keyset modifier.
412  * @param {!KeysetModifier} The modifier to apply.
413  */
414 function applyKeysetModifier(modifier) {
415   if (modifier == KeysetModifier.NONE) {
416     return;
417   }
418   var activeView = getActiveView();
419   if (modifier == KeysetModifier.SHIFT) {
420     // Set state of shift key.
421     var leftShift = activeView.querySelector('#ShiftLeft');
422     assertTrue(!!leftShift, 'Unable to find left shift key');
423     var currentShiftState = !!leftShift.querySelector(
424         '.inputview-special-key-highlight');
425     if (!currentShiftState) {
426       mockTap(leftShift);
427     }
428   } else if (modifier == KeysetModifier.SYMBOL) {
429     var switchKey = findKey('?123', 'spaceKeyrow');
430     assertTrue(!!switchKey, 'Unable to find symbol transition key');
431     // Switch to symbol keyset.
432     mockTap(switchKey);
433   } else {
434     var switchKey = findKey('~[<', 'row3');
435     assertTrue(!!switchKey, 'Unable to find more transition key');
436     // Switch to more keyset.
437     mockTap(switchKey);
438   }