| 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 |