OLD | NEW |
| (Empty) |
1 /* | |
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. | |
5 */ | |
6 | |
7 var mockController; | |
8 var mockTimer; | |
9 var realSetTimeout; | |
10 var sendMessage; | |
11 var DEFAULT_CONTEXT_ID = 1; | |
12 var LONGPRESS_DELAY = 1100; | |
13 | |
14 /** | |
15 * The enumeration of message types. This should be kept in sync with the | |
16 * InputView enums. | |
17 * @const | |
18 * @enum {number} | |
19 */ | |
20 var Type = { | |
21 COMMIT_TEXT: 7, | |
22 }; | |
23 | |
24 | |
25 /** | |
26 * Create mocks for the virtualKeyboardPrivate API. Any tests that trigger API | |
27 * calls must set expectations for call signatures. | |
28 */ | |
29 function setUp() { | |
30 realSetTimeout = window.setTimeout; | |
31 mockController = new MockController(); | |
32 mockTimer = new MockTimer(); | |
33 mockTimer.install(); | |
34 | |
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, | |
41 observedEvent.text, | |
42 'Mismatched commit text.'); | |
43 }; | |
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) | |
49 else | |
50 console.error("Unknown message type: " + msg.type); | |
51 }; | |
52 chrome.input.ime.commitText.validateCall = validateCommit; | |
53 } | |
54 | |
55 function RunTest(testFn, testDoneCallback) { | |
56 var pollTillReady = function() { | |
57 if (window.isKeyboardReady()) { | |
58 testFn(); | |
59 testDoneCallback(); | |
60 } else { | |
61 window.startTest(); | |
62 realSetTimeout(pollTillReady, 100); | |
63 } | |
64 } | |
65 pollTillReady(); | |
66 } | |
67 | |
68 /** | |
69 * Verify that API calls match expectations. | |
70 */ | |
71 function tearDown() { | |
72 mockController.verifyMocks(); | |
73 mockController.reset(); | |
74 chrome.runtime.sendMessage = sendMessage; | |
75 mockTimer.uninstall(); | |
76 } | |
77 | |
78 /** | |
79 * Checks whether the element is currently being displayed on screen. | |
80 * @param {Object} The object to check. | |
81 * @return {boolean} | |
82 */ | |
83 function isActive(el) { | |
84 return window.getComputedStyle(el).display != "none"; | |
85 } | |
86 | |
87 (function(exports) { | |
88 | |
89 /** | |
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. | |
94 */ | |
95 var KEY_IDS = { | |
96 'a' : { | |
97 'us' : '#\\31 01kbd-k-29', | |
98 'us.compact.qwerty' : '#compactkbd-k-key-11', | |
99 }, | |
100 'c' : { | |
101 'us' : '#\\31 01kbd-k-44', | |
102 'us.compact.qwerty' : '#compactkbd-k-key-24', | |
103 }, | |
104 'd' : { | |
105 'us' : '#\\31 01kbd-k-31', | |
106 'us.compact.qwerty' : '#compactkbd-k-key-13', | |
107 }, | |
108 'e' : { | |
109 'us' : '#\\31 01kbd-k-17', | |
110 'us.compact.qwerty': '#compactkbd-k-key-2', | |
111 }, | |
112 'l' : { | |
113 'us' : '#\\31 01kbd-k-37', | |
114 'us.compact.qwerty' : '#compactkbd-k-key-19', | |
115 }, | |
116 'p' : { | |
117 'us' : '#\\31 01kbd-k-24', | |
118 'us.compact.qwerty' : '#compactkbd-k-key-9', | |
119 }, | |
120 'leftshift' : { | |
121 'us' : '#\\31 01kbd-k-41', | |
122 'us.compact.qwerty' : '#compactkbd-k-21', | |
123 }, | |
124 "capslock" : { | |
125 'us' : '#\\31 01kbd-k-28', | |
126 } | |
127 }; | |
128 | |
129 /** | |
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. | |
133 */ | |
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]; | |
140 } | |
141 | |
142 /** | |
143 * Returns the current layout id. | |
144 * @return {string} | |
145 */ | |
146 var getLayoutId_ = function() { | |
147 // TODO(rsadam@): Generalize this. | |
148 var id = window.location.search.split("id=")[1]; | |
149 assertTrue(!!id, "No layout found."); | |
150 return id; | |
151 } | |
152 | |
153 /** | |
154 * Returns the layout with the id provided. Periods in the id are replaced by | |
155 * hyphens. | |
156 * @param id {string} id The layout id. | |
157 * @return {object} | |
158 */ | |
159 | |
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); | |
165 return layout; | |
166 } | |
167 | |
168 /** | |
169 * Returns the layout with the id provided. Periods in the id are replaced by | |
170 * hyphens. | |
171 * @param id {string} id The layout id. | |
172 * @return {object} | |
173 */ | |
174 | |
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); | |
180 return layout; | |
181 } | |
182 | |
183 /** | |
184 * Returns the key object corresponding to the character. | |
185 * @return {string} char The character. | |
186 */ | |
187 var getKey_ = function(char) { | |
188 var layoutId = getLayoutId(); | |
189 var layout = getLayout_(layoutId); | |
190 | |
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); | |
195 | |
196 assertEquals(1, visible.length, | |
197 "Expect exactly one visible key for char: " + char); | |
198 return visible[0]; | |
199 } | |
200 | |
201 exports.getKey = getKey_; | |
202 exports.getLayoutId = getLayoutId_; | |
203 })(this); | |
204 | |
205 /** | |
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. | |
209 */ | |
210 function generateMouseEvent(target, type) { | |
211 var e = new MouseEvent(type, {bubbles:true, cancelable:true}); | |
212 target.dispatchEvent(e); | |
213 } | |
214 | |
215 /** | |
216 * Mocks a key type using the mouse. | |
217 * @param {Object} key The key to click on. | |
218 */ | |
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'); | |
226 } | |
227 | |
228 /** | |
229 * Mocks a character type using the mouse. Expects the character will be | |
230 * committed. | |
231 * @param {String} char The character to type. | |
232 */ | |
233 function mockMouseType(char) { | |
234 var send = chrome.input.ime.commitText; | |
235 send.addExpectation({ | |
236 contextId: DEFAULT_CONTEXT_ID, | |
237 text: char, | |
238 }); | |
239 var key = getKey(char); | |
240 mockMouseTypeOnKey(key); | |
241 } | |
242 | |
243 /** | |
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. | |
247 */ | |
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(); | |
254 e.pageX = rect.left; | |
255 e.pageY = rect.top; | |
256 target.dispatchEvent(e); | |
257 } | |
258 | |
259 /** | |
260 * Mocks a character type using touch. | |
261 * @param {String} char The expected character. | |
262 */ | |
263 function mockTouchType(char) { | |
264 var send = chrome.input.ime.commitText; | |
265 send.addExpectation({ | |
266 contextId: DEFAULT_CONTEXT_ID, | |
267 text: char, | |
268 }); | |
269 var key = getKey(char); | |
270 generateTouchEvent(key, 'touchstart'); | |
271 generateTouchEvent(key, 'touchend'); | |
272 } | |
273 | |
274 /** | |
275 * Returns, if present, the active alternate key container. | |
276 * @return {?Object} | |
277 */ | |
278 function getActiveAltContainer() { | |
279 // TODO(rsadam): Simplify once code refactor to remove unneeded containers is | |
280 // complete. | |
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; | |
285 } | |
286 | |
287 /** | |
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. | |
292 */ | |
293 function mockLongpress(char, altKeys, index) { | |
294 var key = getKey(char); | |
295 | |
296 generateTouchEvent(key, 'touchstart'); | |
297 mockTimer.tick(LONGPRESS_DELAY); | |
298 | |
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); | |
307 } | |
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], | |
313 }); | |
314 // TODO(rsadam:) Add support for touch move. | |
315 generateTouchEvent(key, 'touchend', true, true) | |
316 assertFalse(isActive(container), "Alt key container was not hidden."); | |
317 } | |
OLD | NEW |