summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chromeos/extensions/virtual_keyboard_browsertest.cc92
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/data/chromeos/virtual_keyboard/typing_test.js58
-rw-r--r--chrome/test/data/chromeos/virtual_keyboard/virtual_keyboard_test_base.js118
-rw-r--r--chrome/test/data/webui/mock_controller.js19
-rw-r--r--ui/keyboard/resources/elements/kb-keyboard.html24
-rw-r--r--ui/webui/resources/js/webui_resource_test.js10
7 files changed, 317 insertions, 5 deletions
diff --git a/chrome/browser/chromeos/extensions/virtual_keyboard_browsertest.cc b/chrome/browser/chromeos/extensions/virtual_keyboard_browsertest.cc
new file mode 100644
index 0000000..3e0adbb
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/virtual_keyboard_browsertest.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/keyboard/keyboard_switches.h"
+
+namespace {
+
+const base::FilePath kWebuiTestDir =
+ base::FilePath(FILE_PATH_LITERAL("webui"));
+
+const base::FilePath kVirtualKeyboardTestDir =
+ base::FilePath(FILE_PATH_LITERAL("chromeos/virtual_keyboard"));
+
+const base::FilePath kMockController =
+ base::FilePath(FILE_PATH_LITERAL("mock_controller.js"));
+
+const base::FilePath kBaseKeyboardTestFramework =
+ base::FilePath(FILE_PATH_LITERAL("virtual_keyboard_test_base.js"));
+
+} // namespace
+
+class VirtualKeyboardBrowserTest : public InProcessBrowserTest {
+ public:
+
+ /**
+ * Ensure that the virtual keyboard is enabled.
+ */
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitch(
+ keyboard::switches::kEnableVirtualKeyboard);
+ }
+
+ /**
+ * Injects javascript in |file| into the keyboard page and runs test methods.
+ */
+ void RunTest(const base::FilePath& file) {
+ ui_test_utils::NavigateToURL(browser(), GURL("chrome://keyboard"));
+
+ content::RenderViewHost* rvh = browser()->tab_strip_model()
+ ->GetActiveWebContents()->GetRenderViewHost();
+ ASSERT_TRUE(rvh);
+
+ // Inject testing scripts.
+ InjectJavascript(kWebuiTestDir, kMockController);
+ InjectJavascript(kVirtualKeyboardTestDir, kBaseKeyboardTestFramework);
+ InjectJavascript(kVirtualKeyboardTestDir, file);
+
+ ASSERT_TRUE(content::ExecuteScript(rvh, utf8_content_));
+
+ // Inject DOM-automation test harness and run tests.
+ std::vector<int> resource_ids;
+ EXPECT_TRUE(ExecuteWebUIResourceTest(rvh, resource_ids));
+ }
+
+ private:
+
+ /**
+ * Injects javascript into the keyboard page. The test |file| is in
+ * directory |dir| relative to the root testing directory.
+ */
+ void InjectJavascript(const base::FilePath& dir,
+ const base::FilePath& file) {
+ base::FilePath path = ui_test_utils::GetTestFilePath(dir, file);
+ std::string library_content;
+ ASSERT_TRUE(base::ReadFileToString(path, &library_content))
+ << path.value();
+ utf8_content_.append(library_content);
+ utf8_content_.append(";\n");
+ }
+
+ std::string utf8_content_;
+};
+
+IN_PROC_BROWSER_TEST_F(VirtualKeyboardBrowserTest, TypingTest) {
+ RunTest(base::FilePath(FILE_PATH_LITERAL("typing_test.js")));
+}
+
+// TODO(kevers|rsadam|bshe): Add UI tests for remaining virtual keyboard
+// functionality.
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 4af63c5..e69dd1e 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -968,6 +968,7 @@
'browser/chromeos/extensions/file_manager/file_browser_private_apitest.cc',
'browser/chromeos/extensions/info_private_apitest.cc',
'browser/chromeos/extensions/input_method_apitest_chromeos.cc',
+ 'browser/chromeos/extensions/virtual_keyboard_browsertest.cc',
'browser/chromeos/extensions/wallpaper_private_apitest.cc',
'browser/chromeos/file_manager/desktop_notifications_browsertest.cc',
'browser/chromeos/file_manager/drive_test_util.cc',
diff --git a/chrome/test/data/chromeos/virtual_keyboard/typing_test.js b/chrome/test/data/chromeos/virtual_keyboard/typing_test.js
new file mode 100644
index 0000000..9479a72
--- /dev/null
+++ b/chrome/test/data/chromeos/virtual_keyboard/typing_test.js
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Mock typing of basic keys. Each keystroke should trigger a pair of
+ * API calls to send viritual key events.
+ * @param {string} label The character being typed.
+ * @param {number} keyCode The legacy key code for the character.
+ * @param {boolean} shiftModifier Indicates if the shift key is being
+ * virtually pressed.
+ * @param {number=} opt_unicode Optional unicode value for the character. Only
+ * required if it cannot be directly calculated from the label.
+ */
+function mockTypeCharacter(label, keyCode, shiftModifier, opt_unicode) {
+ var key = findKey(label);
+ assertTrue(!!key, 'Unable to find key labelled "' + label + '".');
+ var unicodeValue = opt_unicode | label.charCodeAt(0);
+ var send = chrome.virtualKeyboardPrivate.sendKeyEvent;
+ send.addExpectation({
+ type: 'keydown',
+ charValue: unicodeValue,
+ keyCode: keyCode,
+ shiftKey: shiftModifier
+ });
+ send.addExpectation({
+ type: 'keyup',
+ charValue: unicodeValue,
+ keyCode: keyCode,
+ shiftKey: shiftModifier
+ });
+ // Fake typing the key.
+ key.down();
+ key.up();
+}
+
+/**
+ * Tests that typing characters on the default lowercase keyboard triggers the
+ * correct sequence of events. The test is run asynchronously since the
+ * keyboard loads keysets dynamically.
+ */
+function testLowercaseKeysetAsync(testDoneCallback) {
+ var runTest = function() {
+ // Keyboard defaults to lowercase.
+ mockTypeCharacter('a', 0x41, false);
+ mockTypeCharacter('s', 0x53, false);
+ mockTypeCharacter('.', 0xBE, false);
+ mockTypeCharacter('\b', 0x08, false, 0x08);
+ mockTypeCharacter('\t', 0x09, false, 0x09);
+ mockTypeCharacter('\n', 0x0D, false, 0x0A);
+ mockTypeCharacter(' ', 0x20, false);
+ testDoneCallback(this.testFailure);
+ };
+ runTest.name = 'testLowercaseKeysetAsync';
+ onKeyboardReady(runTest);
+}
diff --git a/chrome/test/data/chromeos/virtual_keyboard/virtual_keyboard_test_base.js b/chrome/test/data/chromeos/virtual_keyboard/virtual_keyboard_test_base.js
new file mode 100644
index 0000000..5a9657f
--- /dev/null
+++ b/chrome/test/data/chromeos/virtual_keyboard/virtual_keyboard_test_base.js
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Queue for running tests asynchronously.
+ */
+function TestRunner() {
+ this.queue = [];
+ keyboard.addEventListener('stateChange', this.onStateChange.bind(this));
+}
+
+TestRunner.prototype = {
+
+ /**
+ * Queues a test to run after the keyboard has finished initializing.
+ * @param {!Function} callback The deferred function call.
+ */
+ append: function(callback) {
+ this.queue.push(callback);
+ },
+
+ /**
+ * Notification of a change in the state of the keyboard. Runs all queued
+ * tests if the keyboard has finished initializing.
+ * @param {Object} event The state change event.
+ */
+ onStateChange: function(event) {
+ if (event.detail.state == 'keysetLoaded') {
+ for (var i = 0; i < this.queue.length; i++) {
+ var callback = this.queue[i];
+ try {
+ callback();
+ } catch(err) {
+ console.error('Failure in test ' + callback.name + '\n' + err);
+ console.log(err.stack);
+ callback.testFailure = true;
+ }
+ }
+ this.queue = [];
+ }
+ },
+};
+
+var testRunner;
+var mockController;
+
+/**
+ * Create mocks for the virtualKeyboardPrivate API. Any tests that trigger API
+ * calls must set expectations for call signatures.
+ */
+function setUp() {
+ testRunner = new TestRunner();
+ mockController = new MockController();
+ mockController.createFunctionMock(chrome.virtualKeyboardPrivate,
+ 'insertText');
+
+ mockController.createFunctionMock(chrome.virtualKeyboardPrivate,
+ 'sendKeyEvent');
+
+ var validateSendCall = function(index, expected, observed) {
+ // Only consider the first argument (VirtualKeyEvent) for the validation of
+ // sendKeyEvent calls.
+ var expectedEvent = expected[0];
+ var observedEvent = observed[0];
+ assertEquals(expectedEvent.type,
+ observedEvent.type,
+ 'Mismatched event types.');
+ assertEquals(expectedEvent.charValue,
+ observedEvent.charValue,
+ 'Mismatched unicode values for character.');
+ assertEquals(expectedEvent.keyCode,
+ observedEvent.keyCode,
+ 'Mismatched key codes.');
+ assertEquals(expectedEvent.shiftKey,
+ observedEvent.shiftKey,
+ 'Mismatched states for shift modifier.');
+ };
+ chrome.virtualKeyboardPrivate.sendKeyEvent.validateCall = validateSendCall;
+
+ // TODO(kevers): Mock additional extension API calls as required.
+}
+
+/**
+ * Verify that API calls match expectations.
+ */
+function tearDown() {
+ mockController.verifyMocks();
+ mockController.reset();
+}
+
+/**
+ * Finds the key on the keyboard with the matching label.
+ * @param {string} label The label in the key.
+ * @return {?kb-key} The key element with matching label or undefined if no
+ * matching key is found.
+ */
+function findKey(label) {
+ var keys = keyboard.querySelectorAll('kb-key');
+ for (var i = 0; i < keys.length; i++) {
+ if (keys[i].charValue == label)
+ return keys[i];
+ }
+}
+
+/**
+ * Triggers a callback function to run post initialization of the virtual
+ * keyboard.
+ * @param {Function} callback The callback function.
+ */
+function onKeyboardReady(callback) {
+ if (keyboard.initialized)
+ callback();
+ else
+ testRunner.append(callback);
+}
diff --git a/chrome/test/data/webui/mock_controller.js b/chrome/test/data/webui/mock_controller.js
index 00ab9b2..0cb981f 100644
--- a/chrome/test/data/webui/mock_controller.js
+++ b/chrome/test/data/webui/mock_controller.js
@@ -65,10 +65,22 @@ MockMethod.prototype = {
this.calls_.length,
'Number of method calls did not match expectation.');
for (var i = 0; i < this.expectations_.length; i++) {
- assertDeepEquals(this.expectations_[i],
- this.calls_[i]);
+ this.validateCall(i, this.expectations_[i], this.calls_[i]);
}
- }
+ },
+
+ /**
+ * Verifies that the observed function arguments match expectations.
+ * Override if strict equality is not required.
+ * @param {number} index Canonical index of the function call. Unused in the
+ * base implementation, but provides context that may be useful for
+ * overrides.
+ * @param {!Array} expected The expected arguments.
+ * @parma {!Array} observed The observed arguments.
+ */
+ validateCall: function(index, expected, observed) {
+ assertDeepEquals(expected, observed);
+ },
};
/**
@@ -138,4 +150,5 @@ MockController.prototype = {
override.parent[override.functionName] = override.originalFunction;
}
},
+
};
diff --git a/ui/keyboard/resources/elements/kb-keyboard.html b/ui/keyboard/resources/elements/kb-keyboard.html
index 361cef3..ab64de4 100644
--- a/ui/keyboard/resources/elements/kb-keyboard.html
+++ b/ui/keyboard/resources/elements/kb-keyboard.html
@@ -133,6 +133,14 @@
var swipeInProgress = false;
/**
+ * A boolean used to track if the keyboard is ready for user input. As
+ * alternate layouts are dynamically loaded, the keyboard may be in a state
+ * where it is not fully initialized until all links, key-sequences, and
+ * imports are fully resolved.
+ */
+ var isReady = false;
+
+ /**
* The enumeration of swipe directions.
* @const
* @type {Enum}
@@ -523,6 +531,9 @@
*/
layoutChanged: function() {
if (!this.selectDefaultKeyset()) {
+ this.isReady = false;
+ this.fire('stateChange', {state: 'loadingKeyset'});
+
// Keyset selection fails if the keysets have not been loaded yet.
var keysets = document.querySelector('#' + this.layout);
if (keysets) {
@@ -556,6 +567,14 @@
},
/**
+ * Indicate if the keyboard is ready for user input.
+ * @type {boolean}
+ */
+ get initialized() {
+ return this.isReady;
+ },
+
+ /**
* Generates fabricated key events to simulate typing on a
* physical keyboard.
* @param {Object} detail Attributes of the key being typed.
@@ -582,6 +601,11 @@
keysetsLoaded = true;
if (keysets[i].isDefault) {
this.keyset = matches[REGEX_KEYSET_INDEX];
+ this.isReady = true;
+ this.fire('stateChange', {
+ state: 'keysetLoaded',
+ keyset: this.keyset,
+ });
return true;
}
}
diff --git a/ui/webui/resources/js/webui_resource_test.js b/ui/webui/resources/js/webui_resource_test.js
index cce993d..7d6ebf9 100644
--- a/ui/webui/resources/js/webui_resource_test.js
+++ b/ui/webui/resources/js/webui_resource_test.js
@@ -156,8 +156,13 @@ function assertDeepEquals(expected, observed, opt_message) {
/**
* Runs the next test in the queue. Reports the test results if the queue is
* empty.
+ * @param {boolean=} opt_asyncTestFailure Optional parameter indicated if the
+ * last asynchronous test failed.
*/
- function continueTesting() {
+ function continueTesting(opt_asyncTestFailure) {
+ if (opt_asyncTestFailure)
+ cleanTestRun = false;
+ var done = false;
if (pendingTearDown) {
pendingTearDown();
pendingTearDown = null;
@@ -179,9 +184,10 @@ function assertDeepEquals(expected, observed, opt_message) {
if (!isAsyncTest)
continueTesting();
} else {
+ done = true;
endTests(cleanTestRun);
}
- if (testCases.length) {
+ if (!done) {
domAutomationController.setAutomationId(1);
domAutomationController.send('PENDING');
}