Skip to content

Commit

Permalink
[Chromedriver] Support Command combined keys to do text editing on Mac
Browse files Browse the repository at this point in the history
We are using ChromeDriver to simulate user inputs in tests, and when we
are doing selecting all text by pressing cmd+a on Mac and ctrl+a on
other platforms, copying text by pressing cmd+c on Mac and ctrl+c on
other platforms, cutting text by pressing cmd+x on Mac and ctrl+x on
other platforms, and pasting text by pressing cmd+v on Mac and ctrl+v on
other platforms. Send text edit command to
ForwardKeyboardEventWithCommands in Devtool InputHandler.

Bug: chromedriver:3462

Change-Id: I4a77006235c161c67a1d18a37ccd6de5072c054a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2228170
Reviewed-by: John Chen <[email protected]>
Commit-Queue: Lan Wei <[email protected]>
Cr-Commit-Position: refs/heads/master@{#783605}
  • Loading branch information
LanWei22 authored and Commit Bot committed Jun 29, 2020
1 parent 76b667f commit f984b9c
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 6 deletions.
38 changes: 38 additions & 0 deletions chrome/test/chromedriver/chrome/web_view_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/test/chromedriver/chrome/browser_info.h"
#include "chrome/test/chromedriver/chrome/cast_tracker.h"
#include "chrome/test/chromedriver/chrome/debugger_tracker.h"
Expand Down Expand Up @@ -677,12 +678,49 @@ Status WebViewImpl::DispatchKeyEvents(const std::vector<KeyEvent>& events,
ui::DomCode dom_code = ui::UsLayoutKeyboardCodeToDomCode(it->key_code);
code = ui::KeycodeConverter::DomCodeToCodeString(dom_code);
}

bool is_ctrl_cmd_key_down = false;
#if defined(OS_MACOSX)
if (it->modifiers & kMetaKeyModifierMask)
is_ctrl_cmd_key_down = true;
#else
if (it->modifiers & kControlKeyModifierMask)
is_ctrl_cmd_key_down = true;
#endif
if (!code.empty())
params.SetString("code", code);
if (!it->key.empty())
params.SetString("key", it->key);
else if (it->is_from_action)
params.SetString("key", it->modified_text);

if (is_ctrl_cmd_key_down) {
std::string command;
if (code == "KeyA") {
command = "SelectAll";
} else if (code == "KeyC") {
command = "Copy";
} else if (code == "KeyX") {
command = "Cut";
} else if (code == "KeyY") {
command = "Redo";
} else if (code == "KeyV") {
if (it->modifiers & kShiftKeyModifierMask)
command = "PasteAndMatchStyle";
else
command = "Paste";
} else if (code == "KeyZ") {
if (it->modifiers & kShiftKeyModifierMask)
command = "Redo";
else
command = "Undo";
}

std::unique_ptr<base::ListValue> command_list(new base::ListValue);
command_list->AppendString(command);
params.SetList("commands", std::move(command_list));
}

if (it->location != 0) {
// The |location| parameter in DevTools protocol only accepts 1 (left
// modifiers) and 2 (right modifiers). For location 3 (numeric keypad),
Expand Down
13 changes: 7 additions & 6 deletions chrome/test/chromedriver/key_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ struct ModifierMaskAndKeyCode {
};

const ModifierMaskAndKeyCode kModifiers[] = {
{ kShiftKeyModifierMask, ui::VKEY_SHIFT },
{ kControlKeyModifierMask, ui::VKEY_CONTROL },
{ kAltKeyModifierMask, ui::VKEY_MENU }
};
{kShiftKeyModifierMask, ui::VKEY_SHIFT},
{kControlKeyModifierMask, ui::VKEY_CONTROL},
{kAltKeyModifierMask, ui::VKEY_MENU},
{kMetaKeyModifierMask, ui::VKEY_COMMAND}};

// Ordered list of all the key codes corresponding to special WebDriver keys.
// These keys are "special" in the sense that their code points are defined by
Expand Down Expand Up @@ -579,8 +579,9 @@ Status ConvertKeysToKeyEvents(const base::string16& client_keys,
}

// Create the key events.
bool necessary_modifiers[3];
for (int i = 0; i < 3; ++i) {
int number_modifiers = base::size(kModifiers);
bool necessary_modifiers[number_modifiers];
for (int i = 0; i < number_modifiers; ++i) {
necessary_modifiers[i] =
all_modifiers & kModifiers[i].mask &&
!(sticky_modifiers & kModifiers[i].mask);
Expand Down
78 changes: 78 additions & 0 deletions chrome/test/chromedriver/test/run_py_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,84 @@ def testReleaseActions(self):
self.assertAlmostEqual(50, events[3]['x'], delta=1)
self.assertAlmostEqual(50, events[3]['y'], delta=1)

def testActionsCtrlCommandKeys(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
self._driver.ExecuteScript('''
document.write('<input type="text" id="text1" value="Hello World" />');
document.write('<br/>')
document.write('<input type="text" id="text2">');
var text1 = document.getElementById("text1");
text1.addEventListener("click", function() {
var text1 = document.getElementById("text1");
text1.value="new text";
});
''')
time.sleep(1)

elem1 = self._driver.FindElement('css selector', '#text1')
elem2 = self._driver.FindElement('css selector', '#text2')
self.assertEquals("Hello World", elem1.GetProperty('value'))

time.sleep(1)

platform = util.GetPlatformName()
modifier_key = u'\uE009'
if platform == 'mac':
modifier_key = u'\uE03D'

# This is a sequence of actions, first move the mouse to input field
# "elem1", then press ctrl/cmd key and 'a' key to select all the text in
# "elem1", and then press 'x' to cut the text and move the mouse to input
# field "elem2" and press 'v' to paste the text, and at the end, we check
# the texts in both input fields to see if the text are cut and pasted
# correctly from "elem1" to "elem2".
actions = ({'actions': [{
'type': 'key',
'id': 'key',
'actions': [
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'keyDown', 'value': modifier_key},
{'type': 'keyDown', 'value': 'a'},
{'type': 'keyUp', 'value': 'a'},
{'type': 'keyDown', 'value': 'x'},
{'type': 'keyUp', 'value': 'x'},
{'type': 'keyUp', 'value': modifier_key},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'keyDown', 'value': modifier_key},
{'type': 'keyDown', 'value': 'v'},
{'type': 'keyUp', 'value': 'v'},
{'type': 'keyUp', 'value': modifier_key}
]}, {
'type':'pointer',
'actions':[{'type': 'pointerMove', 'x': 0, 'y': 0, 'origin': elem1},
{'type': 'pointerDown', 'button': 0},
{'type': 'pointerUp', 'button': 0},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pointerMove', 'x': 0, 'y': 0, 'origin': elem2},
{'type': 'pointerDown', 'button': 0},
{'type': 'pointerUp', 'button': 0},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'},
{'type': 'pause'}],
'parameters': {'pointerType': 'mouse'},
'id': 'pointer1'}
]})
self._driver.PerformActions(actions)
time.sleep(1)
self.assertEquals("", elem1.GetProperty('value'))
self.assertEquals("new text", elem2.GetProperty('value'))
time.sleep(1)

def testPageLoadStrategyIsNormalByDefault(self):
self.assertEquals('normal',
self._driver.capabilities['pageLoadStrategy'])
Expand Down

0 comments on commit f984b9c

Please sign in to comment.