Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated Keyboard Input #10113

Merged
merged 9 commits into from Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/_writing-tests/testdriver.md
Expand Up @@ -19,6 +19,7 @@ NB: presently, testdriver.js only works in the top-level test browsing
context (and not therefore in any frame or window opened from it).

### `test_driver.click(element)`
#### `element: a DOM Element object`

This function causes a click to occur on the target element (an
`Element` object), potentially scrolling the document to make it
Expand All @@ -30,5 +31,19 @@ Note that if the element to be clicked does not have a unique ID, the
document must not have any DOM mutations made between the function
being called and the promise settling.

### `test_driver.send_keys(element, keys)`
#### `element: a DOM Element object`
#### `keys: string to send to the element`

This function causes the string `keys` to be send to the target
element (an `Element` object), potentially scrolling the document to
make it possible to send keys. It returns a `Promise` that resolves
after the keys have been send or rejects if the keys cannot be sent
to the element.

Note that if the element that's keys need to be send to does not have
a unique ID, the document must not have any DOM mutations made
between the function being called and the promise settling.


[testharness]: {{ site.baseurl }}{% link _writing-tests/testharness.md %}
Expand Up @@ -7,12 +7,8 @@
<meta assert="assert" content="Check if the key events received by document are targeted at the element when it is focused">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<h2>Steps:</h2>
<ol>
<li>Input any character into the textbox by keyboard in 10 seconds.</li>
</ol>
<h2>Expect results:</h2>
<p>PASS</p>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<div id="log"></div>
<input id="test">
<script>
Expand Down Expand Up @@ -40,4 +36,10 @@ <h2>Expect results:</h2>
assert_equals(evt.target, testEle, "The keyup events must be targeted at the input element.");
});

var input_element = document.getElementById("test");

t1.step(function() {
test_driver.send_keys(input_element, "a");
});

</script>
19 changes: 19 additions & 0 deletions infrastructure/testdriver/send_keys.html
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>TestDriver click method</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>

<input type="text" id="text">Button</button>

<script>
async_test(t => {
let text_box = document.getElementById("text");
test_driver
.send_keys(text_box, "Hello, wpt!")
.then(() => t.done())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check this actually worked here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still seems like a reasonable comment, @kereliuk!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, GitHub is just showing me random diffs that aren't of the right commit when I first click here. o_O

.catch(t.unreached_func("click failed"));
});
</script>
47 changes: 47 additions & 0 deletions resources/testdriver.js
Expand Up @@ -83,6 +83,42 @@
return window.test_driver_internal.click(element,
{x: centerPoint[0],
y: centerPoint[1]});
},

/**
* Send keys to an element
*
* This matches the behaviour of the {@link
* https://w3c.github.io/webdriver/webdriver-spec.html#element-send-keys|WebDriver
* Send Keys command}.
*
* @param {Element} element - element to send keys to
* @param {String} keys - keys to send to the element
* @returns {Promise} fulfilled after keys are sent, or rejected in
* the cases the WebDriver command errors
*/
send_keys: function(element, keys) {
if (window.top !== window) {
return Promise.reject(new Error("can only send keys in top-level window"));
}

if (!window.document.contains(element)) {
return Promise.reject(new Error("element in different document or shadow tree"));
}

if (!inView(element)) {
element.scrollIntoView({behavior: "instant",
block: "end",
inline: "nearest"});
}

var pointerInteractablePaintTree = getPointerInteractablePaintTree(element);
if (pointerInteractablePaintTree.length === 0 ||
!element.contains(pointerInteractablePaintTree[0])) {
return Promise.reject(new Error("element send_keys intercepted error"));
}

return window.test_driver_internal.send_keys(element, keys);
}
};

Expand All @@ -96,6 +132,17 @@
*/
click: function(element, coords) {
return Promise.reject(new Error("unimplemented"));
},

/**
* Triggers a user-initated click
*
* @param {Element} element - element to be clicked
* @param {String} keys - keys to send to the element
* @returns {Promise} fulfilled after keys are sent or rejected if click fails
*/
send_keys: function(element, keys) {
return Promise.reject(new Error("unimplemented"));
}
};
})();
20 changes: 18 additions & 2 deletions tools/wptrunner/wptrunner/executors/base.py
Expand Up @@ -485,7 +485,8 @@ def __init__(self, logger, protocol, test_window):
}

self.actions = {
"click": ClickAction(self.logger, self.protocol)
"click": ClickAction(self.logger, self.protocol),
"send_keys": SendKeysAction(self.logger, self.protocol)
}

def __call__(self, result):
Expand Down Expand Up @@ -528,7 +529,6 @@ def process_action(self, url, payload):
def _send_message(self, message_type, status, message=None):
self.protocol.testdriver.send_message(message_type, status, message=message)


class ClickAction(object):
def __init__(self, logger, protocol):
self.logger = logger
Expand All @@ -543,3 +543,19 @@ def __call__(self, payload):
raise ValueError("Selector matches multiple elements")
self.logger.debug("Clicking element: %s" % selector)
self.protocol.click.element(elements[0])

class SendKeysAction(object):
def __init__(self, logger, protocol):
self.logger = logger
self.protocol = protocol

def __call__(self, payload):
selector = payload["selector"]
keys = payload["keys"]
elements = self.protocol.select.elements_by_selector(selector)
if len(elements) == 0:
raise ValueError("Selector matches no elements")
elif len(elements) > 1:
raise ValueError("Selector matches multiple elements")
self.logger.debug("Sending keys to element: %s" % selector)
self.protocol.send_keys.send_keys(elements[0], keys)
9 changes: 9 additions & 0 deletions tools/wptrunner/wptrunner/executors/executorselenium.py
Expand Up @@ -19,6 +19,7 @@
Protocol,
SelectorProtocolPart,
ClickProtocolPart,
SendKeysProtocolPart,
TestDriverProtocolPart)
from ..testrunner import Stop

Expand Down Expand Up @@ -134,6 +135,13 @@ def setup(self):
def element(self, element):
return element.click()

class SeleniumSendKeysProtocolPart(SendKeysProtocolPart):
def setup(self):
self.webdriver = self.parent.webdriver

def send_keys(self, element, keys):
return element.send_keys(keys)


class SeleniumTestDriverProtocolPart(TestDriverProtocolPart):
def setup(self):
Expand All @@ -154,6 +162,7 @@ class SeleniumProtocol(Protocol):
SeleniumTestharnessProtocolPart,
SeleniumSelectorProtocolPart,
SeleniumClickProtocolPart,
SeleniumSendKeysProtocolPart,
SeleniumTestDriverProtocolPart]

def __init__(self, executor, browser, capabilities, **kwargs):
Expand Down
14 changes: 14 additions & 0 deletions tools/wptrunner/wptrunner/executors/protocol.py
Expand Up @@ -259,6 +259,20 @@ def element(self, element):
:param element: A protocol-specific handle to an element."""
pass

class SendKeysProtocolPart(ProtocolPart):
"""Protocol part for performing trusted clicks"""
__metaclass__ = ABCMeta

name = "send_keys"

@abstractmethod
def send_keys(self, element, keys):
"""Send keys to a specific element.

:param element: A protocol-specific handle to an element.
:param keys: A protocol-specific handle to a string of input keys."""
pass


class TestDriverProtocolPart(ProtocolPart):
"""Protocol part that implements the basic functionality required for
Expand Down
10 changes: 10 additions & 0 deletions tools/wptrunner/wptrunner/testdriver-extra.js
Expand Up @@ -60,4 +60,14 @@
window.opener.postMessage({"type": "action", "action": "click", "selector": selector}, "*");
return pending_promise;
};

window.test_driver_internal.send_keys = function(element, keys) {
const selector = get_selector(element);
const pending_promise = new Promise(function(resolve, reject) {
pending_resolve = resolve;
pending_reject = reject;
});
window.opener.postMessage({"type": "action", "action": "send_keys", "selector": selector, "keys": keys}, "*");
return pending_promise;
};
})();