is not held down, this method has no effect.
+
+ Arguments:
+ key: The name of a key to release.
+
+ Example:
+
+ >>> b = Browser()
+ >>> Keyboard(b.driver).down('SHIFT')
+ >>> Keyboard(b.driver).up('SHIFT')
+ """
+ chain = ActionChains(self.driver)
+ chain = self._resolve_key_up_action(chain, key)
+ chain.perform()
+ return self
+
+ def press(self, key_pattern: str, delay: int = 0) -> "Keyboard":
+ """Hold and release a key pattern.
+
+ Key patterns are strings of key names separated by '+'.
+ The following are examples of key patterns:
+ - 'CONTROL'
+ - 'CONTROL+a'
+ - 'CONTROL+a+BACKSPACE+b'
+
+ Arguments:
+ key_pattern: Pattern of keys to hold and release.
+ delay: Time, in seconds, to wait between the hold and release.
+
+ Example:
+
+ >>> b = Browser()
+ >>> Keyboard(b.driver).press('CONTROL+a')
+ """
+ keys_names = key_pattern.split("+")
+
+ chain = ActionChains(self.driver)
+
+ for item in keys_names:
+ chain = self._resolve_key_down_action(chain, item)
+
+ if delay:
+ chain = chain.pause(delay)
+
+ for item in keys_names:
+ chain = self._resolve_key_up_action(chain, item)
+
+ chain.perform()
+
+ return self
diff --git a/tests/element.py b/tests/element.py
index 978c8087f..83ea922e0 100644
--- a/tests/element.py
+++ b/tests/element.py
@@ -1,6 +1,7 @@
# Copyright 2012 splinter authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
+from .skip_if import skip_if_zope, skip_if_django, skip_if_flask
class ElementTest:
@@ -29,3 +30,32 @@ def test_element_html(self):
assert (
self.browser.find_by_id("html-property").html == 'inner inner text
html test'
)
+
+ @skip_if_zope
+ @skip_if_django
+ @skip_if_flask
+ def test_element_press_modifier(self):
+ elem = self.browser.find_by_css("[name='q']")
+ elem.fill("hellox")
+ elem.press("BACKSPACE")
+
+ assert elem.value == "hello"
+
+ @skip_if_zope
+ @skip_if_django
+ @skip_if_flask
+ def test_element_press_key(self):
+ elem = self.browser.find_by_css("[name='q']")
+ elem.fill("hellox")
+ elem.press("a")
+
+ assert elem.value == "helloxa"
+
+ @skip_if_zope
+ @skip_if_django
+ @skip_if_flask
+ def test_element_press_combo(self):
+ elem = self.browser.find_by_css("[name='q']")
+ elem.press("SHIFT+a+BACKSPACE+b")
+
+ assert elem.value == "B"
diff --git a/tests/form_elements.py b/tests/form_elements.py
index bbdb08be2..7e25ecf64 100644
--- a/tests/form_elements.py
+++ b/tests/form_elements.py
@@ -7,26 +7,7 @@
import pytest
from splinter.exceptions import ElementDoesNotExist
-
-
-def skip_if_zope(f):
- def wrapper(self, *args, **kwargs):
- if self.__class__.__name__ == "TestZopeTestBrowserDriver":
- return pytest.skip("skipping this test for zope testbrowser")
- else:
- f(self, *args, **kwargs)
-
- return wrapper
-
-
-def skip_if_django(f):
- def wrapper(self, *args, **kwargs):
- if self.__class__.__name__ == "TestDjangoClientDriver":
- return pytest.skip("skipping this test for django")
- else:
- f(self, *args, **kwargs)
-
- return wrapper
+from .skip_if import skip_if_zope, skip_if_django
class FormElementsTest:
diff --git a/tests/keyboard.py b/tests/keyboard.py
new file mode 100644
index 000000000..575932326
--- /dev/null
+++ b/tests/keyboard.py
@@ -0,0 +1,40 @@
+from splinter.driver.webdriver import Keyboard
+
+
+class KeyboardTest:
+ def test_keyboard_down_modifier(self):
+ keyboard = Keyboard(self.browser.driver)
+
+ keyboard.down("CONTROL")
+
+ elem = self.browser.find_by_css("#keypress_detect")
+
+ assert elem.first
+
+ def test_keyboard_up_modifier(self):
+ keyboard = Keyboard(self.browser.driver)
+
+ keyboard.down("CONTROL")
+ keyboard.up("CONTROL")
+
+ elem = self.browser.find_by_css("#keyup_detect")
+
+ assert elem.first
+
+ def test_keyboard_press_modifier(self):
+ keyboard = Keyboard(self.browser.driver)
+
+ keyboard.press("CONTROL")
+
+ elem = self.browser.find_by_css("#keyup_detect")
+
+ assert elem.first
+
+ def test_element_press_combo(self):
+ keyboard = Keyboard(self.browser.driver)
+
+ keyboard.press("CONTROL+a")
+
+ elem = self.browser.find_by_css("#keypress_detect_a")
+
+ assert elem.first
diff --git a/tests/skip_if.py b/tests/skip_if.py
new file mode 100644
index 000000000..3875515bf
--- /dev/null
+++ b/tests/skip_if.py
@@ -0,0 +1,31 @@
+import pytest
+
+
+def skip_if_zope(f):
+ def wrapper(self, *args, **kwargs):
+ if self.__class__.__name__ == "TestZopeTestBrowserDriver":
+ return pytest.skip("skipping this test for zope testbrowser")
+ else:
+ f(self, *args, **kwargs)
+
+ return wrapper
+
+
+def skip_if_django(f):
+ def wrapper(self, *args, **kwargs):
+ if self.__class__.__name__ == "TestDjangoClientDriver":
+ return pytest.skip("skipping this test for django")
+ else:
+ f(self, *args, **kwargs)
+
+ return wrapper
+
+
+def skip_if_flask(f):
+ def wrapper(self, *args, **kwargs):
+ if self.__class__.__name__ == "TestFlaskClientDriver":
+ return pytest.skip("skipping this test for flask")
+ else:
+ f(self, *args, **kwargs)
+
+ return wrapper
diff --git a/tests/static/index.html b/tests/static/index.html
index a8265e73e..9e547c1a1 100644
--- a/tests/static/index.html
+++ b/tests/static/index.html
@@ -32,6 +32,22 @@
addShadowRoot();
}, false);
+ document.onkeydown = function (e) {
+ if (e.key === "Control") {
+ $('body').append('Added when "Control" key is pressed down.
');
+ }
+ if (e.key === "a") {
+ $('body').append('Added when "a" key is pressed down.
');
+ }
+ };
+
+ document.onkeyup = function (e) {
+ e = e || window.event;
+ if (e.key === "Control") {
+ $('body').append('Added when "Control" key is released.
');
+ }
+ };
+
$(document).ready(function() {
$(".draggable").draggable();
$(".droppable").droppable({
diff --git a/tests/test_webdriver_chrome.py b/tests/test_webdriver_chrome.py
index 07426abc7..35e0bada6 100644
--- a/tests/test_webdriver_chrome.py
+++ b/tests/test_webdriver_chrome.py
@@ -5,10 +5,11 @@
from .base import get_browser
from .base import WebDriverTests
+from .keyboard import KeyboardTest
from .fake_webapp import EXAMPLE_APP
-class TestChromeBrowser(WebDriverTests):
+class TestChromeBrowser(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("chrome", fullscreen=False)
@@ -20,7 +21,7 @@ def visit_example_app(self, request):
self.browser.visit(EXAMPLE_APP)
-class TestChromeBrowserFullscreen(WebDriverTests):
+class TestChromeBrowserFullscreen(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("chrome", fullscreen=True)
diff --git a/tests/test_webdriver_edge_chromium.py b/tests/test_webdriver_edge_chromium.py
index b88b6d004..d86994712 100644
--- a/tests/test_webdriver_edge_chromium.py
+++ b/tests/test_webdriver_edge_chromium.py
@@ -5,10 +5,11 @@
from .base import get_browser
from .base import WebDriverTests
+from .keyboard import KeyboardTest
from .fake_webapp import EXAMPLE_APP
-class TestEdgeChromiumBrowser(WebDriverTests):
+class TestEdgeChromiumBrowser(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("edge", fullscreen=False)
@@ -20,7 +21,7 @@ def visit_example_app(self, request):
self.browser.visit(EXAMPLE_APP)
-class TestEdgeChromiumBrowserFullscreen(WebDriverTests):
+class TestEdgeChromiumBrowserFullscreen(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("edge", fullscreen=True)
diff --git a/tests/test_webdriver_firefox.py b/tests/test_webdriver_firefox.py
index 6debe3828..8de7eb419 100644
--- a/tests/test_webdriver_firefox.py
+++ b/tests/test_webdriver_firefox.py
@@ -7,11 +7,12 @@
from .base import get_browser
from .base import WebDriverTests
+from .keyboard import KeyboardTest
from .fake_webapp import EXAMPLE_APP
from splinter.config import Config
-class TestFirefoxBrowser(WebDriverTests):
+class TestFirefoxBrowser(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("firefox", fullscreen=False)
@@ -22,7 +23,7 @@ def visit_example_app(self, request):
self.browser.visit(EXAMPLE_APP)
-class TestFirefoxBrowserFullScreen(WebDriverTests):
+class TestFirefoxBrowserFullScreen(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = get_browser("firefox", fullscreen=True)
diff --git a/tests/test_webdriver_remote.py b/tests/test_webdriver_remote.py
index 4987349aa..0333b4f18 100644
--- a/tests/test_webdriver_remote.py
+++ b/tests/test_webdriver_remote.py
@@ -7,6 +7,7 @@
import pytest
from .base import WebDriverTests
+from .keyboard import KeyboardTest
from .fake_webapp import EXAMPLE_APP
from splinter import Browser
@@ -21,7 +22,7 @@ def selenium_server_is_running():
return "WebDriver Hub" in page_contents
-class TestRemoteBrowserFirefox(WebDriverTests):
+class TestRemoteBrowserFirefox(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = Browser("remote", browser="firefox")
@@ -41,7 +42,7 @@ def test_should_be_able_to_change_user_agent(self):
pass
-class TestRemoteBrowserChrome(WebDriverTests):
+class TestRemoteBrowserChrome(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
request.cls.browser = Browser("remote", browser="chrome")
@@ -62,7 +63,7 @@ def test_should_be_able_to_change_user_agent(self):
@pytest.mark.macos
-class TestRemoteBrowserSafari(WebDriverTests):
+class TestRemoteBrowserSafari(WebDriverTests, KeyboardTest):
@pytest.fixture(autouse=True, scope="class")
def setup_browser(self, request):
# test with statement. It can't be used as simple test