Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit 6a56eba

Browse files
committed
Implement W3C compatible RemoteKeyboard (fixes #698)
1 parent bd15029 commit 6a56eba

8 files changed

+192
-32
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ before_script:
169169
script:
170170
- if [ -n "$SAUCELABS" ]; then EXCLUDE_GROUP+="exclude-saucelabs,"; fi
171171
- if [ "$BROWSER_NAME" = "MicrosoftEdge" ]; then EXCLUDE_GROUP+="exclude-edge,"; fi
172+
- if [ "$BROWSER_NAME" = "firefox" ]; then EXCLUDE_GROUP+="exclude-firefox,"; fi
172173
- if [ -n "$EXCLUDE_GROUP" ]; then EXTRA_PARAMS+=" --exclude-group $EXCLUDE_GROUP"; fi
173174
- ./vendor/bin/phpunit --coverage-clover ./logs/coverage-clover.xml $EXTRA_PARAMS
174175

lib/Remote/RemoteKeyboard.php

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
namespace Facebook\WebDriver\Remote;
1717

18+
use Facebook\WebDriver\WebDriver;
1819
use Facebook\WebDriver\WebDriverKeyboard;
1920
use Facebook\WebDriver\WebDriverKeys;
2021

@@ -23,17 +24,21 @@
2324
*/
2425
class RemoteKeyboard implements WebDriverKeyboard
2526
{
26-
/**
27-
* @var RemoteExecuteMethod
28-
*/
27+
/** @var RemoteExecuteMethod */
2928
private $executor;
29+
/** @var WebDriver */
30+
private $driver;
31+
/** @var bool */
32+
private $isW3cCompliant;
3033

3134
/**
32-
* @param RemoteExecuteMethod $executor
35+
* @param bool $isW3cCompliant
3336
*/
34-
public function __construct(RemoteExecuteMethod $executor)
37+
public function __construct(RemoteExecuteMethod $executor, WebDriver $driver, $isW3cCompliant = false)
3538
{
3639
$this->executor = $executor;
40+
$this->driver = $driver;
41+
$this->isW3cCompliant = $isW3cCompliant;
3742
}
3843

3944
/**
@@ -43,9 +48,14 @@ public function __construct(RemoteExecuteMethod $executor)
4348
*/
4449
public function sendKeys($keys)
4550
{
46-
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
47-
'value' => WebDriverKeys::encode($keys),
48-
]);
51+
if ($this->isW3cCompliant) {
52+
$activeElement = $this->driver->switchTo()->activeElement();
53+
$activeElement->sendKeys($keys);
54+
} else {
55+
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
56+
'value' => WebDriverKeys::encode($keys),
57+
]);
58+
}
4959

5060
return $this;
5161
}
@@ -59,9 +69,21 @@ public function sendKeys($keys)
5969
*/
6070
public function pressKey($key)
6171
{
62-
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
63-
'value' => [(string) $key],
64-
]);
72+
if ($this->isW3cCompliant) {
73+
$this->executor->execute(DriverCommand::ACTIONS, [
74+
'actions' => [
75+
[
76+
'type' => 'key',
77+
'id' => 'keyboard',
78+
'actions' => [['type' => 'keyDown', 'value' => $key]],
79+
],
80+
],
81+
]);
82+
} else {
83+
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
84+
'value' => [(string) $key],
85+
]);
86+
}
6587

6688
return $this;
6789
}
@@ -75,9 +97,21 @@ public function pressKey($key)
7597
*/
7698
public function releaseKey($key)
7799
{
78-
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
79-
'value' => [(string) $key],
80-
]);
100+
if ($this->isW3cCompliant) {
101+
$this->executor->execute(DriverCommand::ACTIONS, [
102+
'actions' => [
103+
[
104+
'type' => 'key',
105+
'id' => 'keyboard',
106+
'actions' => [['type' => 'keyUp', 'value' => $key]],
107+
],
108+
],
109+
]);
110+
} else {
111+
$this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [
112+
'value' => [(string) $key],
113+
]);
114+
}
81115

82116
return $this;
83117
}

lib/Remote/RemoteWebDriver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ public function getMouse()
474474
public function getKeyboard()
475475
{
476476
if (!$this->keyboard) {
477-
$this->keyboard = new RemoteKeyboard($this->getExecuteMethod());
477+
$this->keyboard = new RemoteKeyboard($this->getExecuteMethod(), $this, $this->isW3cCompliant);
478478
}
479479

480480
return $this->keyboard;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
// Copyright 2004-present Facebook. All Rights Reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
namespace Facebook\WebDriver;
17+
18+
use Facebook\WebDriver\Remote\WebDriverBrowserType;
19+
20+
/**
21+
* @covers \Facebook\WebDriver\Remote\RemoteKeyboard
22+
*/
23+
class RemoteKeyboardTest extends WebDriverTestCase
24+
{
25+
/**
26+
* @group exclude-firefox
27+
* Firefox does not properly support keyboard actions:
28+
* https://github.com/mozilla/geckodriver/issues/245
29+
* https://github.com/mozilla/geckodriver/issues/646
30+
* https://github.com/mozilla/geckodriver/issues/944
31+
* @group exclude-edge
32+
*/
33+
public function testShouldPressSendAndReleaseKeys()
34+
{
35+
if ($this->desiredCapabilities->getBrowserName() === WebDriverBrowserType::HTMLUNIT) {
36+
$this->markTestSkipped('Not peorperly supported by HtmlUnit browser');
37+
}
38+
39+
$this->driver->get($this->getTestPageUrl('events.html'));
40+
41+
$this->driver->getKeyboard()->sendKeys('ab');
42+
$this->driver->getKeyboard()->pressKey(WebDriverKeys::SHIFT);
43+
44+
$this->driver->getKeyboard()->sendKeys('cd' . WebDriverKeys::NULL . 'e');
45+
46+
$this->driver->getKeyboard()->pressKey(WebDriverKeys::SHIFT);
47+
$this->driver->getKeyboard()->pressKey('f');
48+
$this->driver->getKeyboard()->releaseKey(WebDriverKeys::SHIFT);
49+
$this->driver->getKeyboard()->releaseKey('f');
50+
51+
if (self::isW3cProtocolBuild()) {
52+
$this->assertEquals(
53+
[
54+
'keydown "a"',
55+
'keyup "a"',
56+
'keydown "b"',
57+
'keyup "b"',
58+
'keydown "Shift"',
59+
'keydown "C"',
60+
'keyup "C"',
61+
'keydown "D"',
62+
'keyup "D"',
63+
'keyup "Shift"',
64+
'keydown "e"',
65+
'keyup "e"',
66+
'keydown "Shift"',
67+
'keydown "F"',
68+
'keyup "Shift"',
69+
'keyup "f"',
70+
],
71+
$this->retrieveLoggedEvents()
72+
);
73+
} else {
74+
$this->assertEquals(
75+
[
76+
'keydown "a"',
77+
'keyup "a"',
78+
'keydown "b"',
79+
'keyup "b"',
80+
'keydown "Shift"',
81+
'keydown "C"',
82+
'keyup "C"',
83+
'keydown "D"',
84+
'keyup "D"',
85+
'keyup "Shift"',
86+
'keydown "e"',
87+
'keyup "e"',
88+
'keydown "Shift"',
89+
'keydown "F"', // pressKey behaves differently on old protocol
90+
'keyup "F"',
91+
'keyup "Shift"',
92+
'keydown "f"',
93+
'keyup "f"',
94+
],
95+
$this->retrieveLoggedEvents()
96+
);
97+
}
98+
}
99+
100+
/**
101+
* @return array
102+
*/
103+
private function retrieveLoggedEvents()
104+
{
105+
$logElement = $this->driver->findElement(WebDriverBy::id('keyboardEventsLog'));
106+
107+
return explode("\n", $logElement->getText());
108+
}
109+
}

tests/functional/WebDriverActionsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public function testShouldDragAndDrop()
166166
*/
167167
private function retrieveLoggedEvents()
168168
{
169-
$logElement = $this->driver->findElement(WebDriverBy::id('log'));
169+
$logElement = $this->driver->findElement(WebDriverBy::id('mouseEventsLog'));
170170

171171
return explode("\n", $logElement->getText());
172172
}

tests/functional/WebDriverAlertTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function testShouldAcceptAlert()
5555

5656
public function testShouldAcceptAndDismissConfirmation()
5757
{
58-
if ($this->desiredCapabilities->getBrowserName() == WebDriverBrowserType::HTMLUNIT) {
58+
if ($this->desiredCapabilities->getBrowserName() === WebDriverBrowserType::HTMLUNIT) {
5959
/** @see https://github.com/SeleniumHQ/htmlunit-driver/issues/14 */
6060
$this->markTestSkipped('Not supported by HtmlUnit browser');
6161
}
@@ -82,7 +82,7 @@ public function testShouldAcceptAndDismissConfirmation()
8282

8383
public function testShouldSubmitPromptText()
8484
{
85-
if ($this->desiredCapabilities->getBrowserName() == WebDriverBrowserType::HTMLUNIT) {
85+
if ($this->desiredCapabilities->getBrowserName() === WebDriverBrowserType::HTMLUNIT) {
8686
/** @see https://github.com/SeleniumHQ/htmlunit-driver/issues/14 */
8787
$this->markTestSkipped('Not supported by HtmlUnit browser');
8888
}

tests/functional/web/events.html

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,30 @@
2828
<li id="item-3" draggable="true">Third item</li>
2929
</ul>
3030

31-
<pre id="log" style="border: 1px solid black; height: 20em; overflow: auto;"></pre>
31+
<pre id="mouseEventsLog" style="border: 1px solid black; height: 20em; overflow: auto;"></pre>
32+
33+
<pre id="keyboardEventsLog" style="border: 1px solid black; height: 20em; overflow: auto;"></pre>
3234

3335
<script>
34-
function logEvent(event) {
35-
var logElement = document.getElementById('log');
36+
function logMouseEvent(event) {
37+
var logElement = document.getElementById('mouseEventsLog');
3638
logElement.innerHTML += '<strong>' + event.type + '</strong> <span>' + event.target.id +'</span><br>';
37-
logElement.scrollTop = logElement.scrollHeight;
39+
console.log(event);
40+
}
41+
42+
function logKeyboardEvent(event) {
43+
var logElement = document.getElementById('keyboardEventsLog');
44+
logElement.innerHTML += '<strong>' + event.type + '</strong> <span>&quot;' + event.key +'&quot;</span>' + "\n";
3845
console.log(event);
3946
}
4047

4148
function handleDragenter(e) {
42-
logEvent(e);
49+
logMouseEvent(e);
4350
e.target.classList.add('active');
4451
}
4552

4653
function handleDragleave(e) {
47-
logEvent(e);
54+
logMouseEvent(e);
4855
e.target.classList.remove('active');
4956
}
5057

@@ -53,7 +60,7 @@
5360
}
5461

5562
function handleDrop(e) {
56-
logEvent(e);
63+
logMouseEvent(e);
5764
e.preventDefault();
5865

5966
var data = e.dataTransfer.getData('text');
@@ -68,18 +75,21 @@
6875
el.addEventListener(
6976
'dragstart',
7077
function (e) {
71-
logEvent(e);
78+
logMouseEvent(e);
7279
e.dataTransfer.setData('text/plain', e.target.id);
7380
}
7481
);
7582

76-
el.addEventListener('click', logEvent);
77-
el.addEventListener('contextmenu', logEvent);
78-
el.addEventListener('dblclick', logEvent);
79-
el.addEventListener('mouseover', logEvent);
80-
el.addEventListener('mousedown', logEvent);
81-
el.addEventListener('mouseup', logEvent);
83+
el.addEventListener('click', logMouseEvent);
84+
el.addEventListener('contextmenu', logMouseEvent);
85+
el.addEventListener('dblclick', logMouseEvent);
86+
el.addEventListener('mouseover', logMouseEvent);
87+
el.addEventListener('mousedown', logMouseEvent);
88+
el.addEventListener('mouseup', logMouseEvent);
8289
}
90+
91+
document.addEventListener('keydown', logKeyboardEvent);
92+
document.addEventListener('keyup', logKeyboardEvent);
8393
</script>
8494

8595
</body>

tests/functional/web/form.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
</label>
1313
<br>
1414

15+
<label>
16+
Text (empty):
17+
<input type="text" name="text" id="input-text-empty" value=""/>
18+
</label>
19+
<br>
20+
1521
<label>
1622
Text disabled:
1723
<input type="text" name="text" id="input-text-disabled" value="Text of disabled input" disabled/>

0 commit comments

Comments
 (0)