2 * Copyright 2014 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.
11 var DEFAULT_CONTEXT_ID = 1;
12 var LONGPRESS_DELAY = 1100;
15 * The enumeration of message types. This should be kept in sync with the
26 * Create mocks for the virtualKeyboardPrivate API. Any tests that trigger API
27 * calls must set expectations for call signatures.
30 realSetTimeout = window.setTimeout;
31 mockController = new MockController();
32 mockTimer = new MockTimer();
35 mockController.createFunctionMock(chrome.input.ime, 'commitText');
36 var validateCommit = function(index, expected, observed) {
37 // Only consider the first argument, the details object.
38 var expectedEvent = expected[0];
39 var observedEvent = observed[0];
40 assertEquals(expectedEvent.text,
42 'Mismatched commit text.');
44 sendMessage = chrome.runtime.sendMessage;
45 chrome.runtime.sendMessage = function(msg){
46 // Forward message to the mocked method.
47 if (msg.type == Type.COMMIT_TEXT)
48 chrome.input.ime.commitText(msg)
50 console.error("Unknown message type: " + msg.type);
52 chrome.input.ime.commitText.validateCall = validateCommit;
55 function RunTest(testFn, testDoneCallback) {
56 var pollTillReady = function() {
57 if (window.isKeyboardReady()) {
62 realSetTimeout(pollTillReady, 100);
69 * Verify that API calls match expectations.
72 mockController.verifyMocks();
73 mockController.reset();
74 chrome.runtime.sendMessage = sendMessage;
75 mockTimer.uninstall();
79 * Checks whether the element is currently being displayed on screen.
80 * @param {Object} The object to check.
83 function isActive(el) {
84 return window.getComputedStyle(el).display != "none";
90 * Map from keys to layout specific key ids. This only contains a small subset
91 * of the keys which are used in testing. The ids are based on the XKB layouts
92 * in GoogleKeyboardInput-xkb.crx. Ids that start with a number are escaped
93 * so that document.querySelector returns the correct element.
97 'us' : '#\\31 01kbd-k-29',
98 'us.compact.qwerty' : '#compactkbd-k-key-11',
101 'us' : '#\\31 01kbd-k-44',
102 'us.compact.qwerty' : '#compactkbd-k-key-24',
105 'us' : '#\\31 01kbd-k-31',
106 'us.compact.qwerty' : '#compactkbd-k-key-13',
109 'us' : '#\\31 01kbd-k-17',
110 'us.compact.qwerty': '#compactkbd-k-key-2',
113 'us' : '#\\31 01kbd-k-37',
114 'us.compact.qwerty' : '#compactkbd-k-key-19',
117 'us' : '#\\31 01kbd-k-24',
118 'us.compact.qwerty' : '#compactkbd-k-key-9',
121 'us' : '#\\31 01kbd-k-41',
122 'us.compact.qwerty' : '#compactkbd-k-21',
125 'us' : '#\\31 01kbd-k-28',
130 * Gets the key id of the specified character.
131 * @param {string} layout The current keyboard layout.
132 * @param {char} char The character to press.
134 var getKeyId_ = function(layout, char) {
135 var lower = char.toLowerCase();
136 assertTrue(!!KEY_IDS[lower], "Cannot find cached key id: " + char);
137 assertTrue(!!KEY_IDS[lower][layout],
138 "Cannot find cached key id: " + char + " in " + layout);
139 return KEY_IDS[lower][layout];
143 * Returns the current layout id.
146 var getLayoutId_ = function() {
147 // TODO(rsadam@): Generalize this.
148 var id = window.location.search.split("id=")[1];
149 assertTrue(!!id, "No layout found.");
154 * Returns the layout with the id provided. Periods in the id are replaced by
156 * @param id {string} id The layout id.
160 var getLayout_ = function(id) {
161 // Escape periods to hyphens.
162 var layoutId = id.replace(/\./g, '-');
163 var layout = document.querySelector('#' + layoutId);
164 assertTrue(!!layout, "Cannot find layout with id: " + layoutId);
169 * Returns the layout with the id provided. Periods in the id are replaced by
171 * @param id {string} id The layout id.
175 var getLayout_ = function(id) {
176 // Escape periods to hyphens.
177 var layoutId = id.replace(/\./g, '-');
178 var layout = document.querySelector('#' + layoutId);
179 assertTrue(!!layout, "Cannot find layout with id: " + layoutId);
184 * Returns the key object corresponding to the character.
185 * @return {string} char The character.
187 var getKey_ = function(char) {
188 var layoutId = getLayoutId();
189 var layout = getLayout_(layoutId);
191 // All keys in the layout that are in the target keys position.
192 var candidates = layout.querySelectorAll(getKeyId_(layoutId, char));
193 assertTrue(candidates.length > 0, "Cannot find key: " + char);
194 var visible = Array.prototype.filter.call(candidates, isActive);
196 assertEquals(1, visible.length,
197 "Expect exactly one visible key for char: " + char);
201 exports.getKey = getKey_;
202 exports.getLayoutId = getLayoutId_;
206 * Generates a mouse event and dispatches it on the target.
207 * @param target {Object} The target of the event.
208 * @param type {String} The type of the mouse event.
210 function generateMouseEvent(target, type) {
211 var e = new MouseEvent(type, {bubbles:true, cancelable:true});
212 target.dispatchEvent(e);
216 * Mocks a key type using the mouse.
217 * @param {Object} key The key to click on.
219 function mockMouseTypeOnKey(key) {
220 generateMouseEvent(key, 'mouseover');
221 generateMouseEvent(key, 'mousedown');
222 generateMouseEvent(key, 'mouseup');
223 generateMouseEvent(key, 'click');
224 generateMouseEvent(key, 'mouseover');
225 generateMouseEvent(key, 'mouseout');
229 * Mocks a character type using the mouse. Expects the character will be
231 * @param {String} char The character to type.
233 function mockMouseType(char) {
234 var send = chrome.input.ime.commitText;
235 send.addExpectation({
236 contextId: DEFAULT_CONTEXT_ID,
239 var key = getKey(char);
240 mockMouseTypeOnKey(key);
244 * Generates a touch event and dispatches it on the target.
245 * @param target {Object} The target of the event.
246 * @param type {String} The type of the touch event.
248 function generateTouchEvent(target, type) {
249 // UIEvent does not allow mocking pageX pageY of the event, so we use the
250 // parent Event class.
251 var e = document.createEvent('Event');
252 e.initEvent(type, true, true);
253 var rect = target.getBoundingClientRect();
256 target.dispatchEvent(e);
260 * Mocks a character type using touch.
261 * @param {String} char The expected character.
263 function mockTouchType(char) {
264 var send = chrome.input.ime.commitText;
265 send.addExpectation({
266 contextId: DEFAULT_CONTEXT_ID,
269 var key = getKey(char);
270 generateTouchEvent(key, 'touchstart');
271 generateTouchEvent(key, 'touchend');
275 * Returns, if present, the active alternate key container.
278 function getActiveAltContainer() {
279 // TODO(rsadam): Simplify once code refactor to remove unneeded containers is
281 var all = document.querySelectorAll('.inputview-altdata-view');
282 var filtered = Array.prototype.filter.call(all, isActive);
283 assertTrue(filtered.length <= 1, "More than one active container.");
284 return filtered.length > 0 ? filtered[0] : null;
288 * Mocks a character long press.
289 * @param {String} char The character to longpress.
290 * @param {Array<string>} altKeys the expected alt keys.
291 * @param {number} index The index of the alt key to select.
293 function mockLongpress(char, altKeys, index) {
294 var key = getKey(char);
296 generateTouchEvent(key, 'touchstart');
297 mockTimer.tick(LONGPRESS_DELAY);
299 var container = getActiveAltContainer();
300 assertTrue(!!container, "Cannot find active alt container.");
301 var options = container.querySelectorAll('.inputview-altdata-key');
302 assertEquals(altKeys.length, options.length,
303 "Unexpected number of alt keys.");
304 // Check all altKeys present and in order specified.
305 for (var i = 0; i < altKeys.length; i++) {
306 assertEquals(altKeys[i], options[i].textContent);
308 // Expect selection to be typed
309 var send = chrome.input.ime.commitText;
310 send.addExpectation({
311 contextId: DEFAULT_CONTEXT_ID,
312 text: altKeys[index],
314 // TODO(rsadam:) Add support for touch move.
315 generateTouchEvent(key, 'touchend', true, true)
316 assertFalse(isActive(container), "Alt key container was not hidden.");