summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webdriver/actions
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/webdriver/actions
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/webdriver/actions')
-rw-r--r--testing/web-platform/tests/webdriver/actions/__init__.py0
-rw-r--r--testing/web-platform/tests/webdriver/actions/conftest.py34
-rw-r--r--testing/web-platform/tests/webdriver/actions/key.py170
-rw-r--r--testing/web-platform/tests/webdriver/actions/mouse.py31
-rw-r--r--testing/web-platform/tests/webdriver/actions/sequence.py31
-rw-r--r--testing/web-platform/tests/webdriver/actions/support/__init__.py0
-rw-r--r--testing/web-platform/tests/webdriver/actions/support/keys.py812
-rw-r--r--testing/web-platform/tests/webdriver/actions/support/refine.py33
-rw-r--r--testing/web-platform/tests/webdriver/actions/support/test_actions_wdspec.html131
9 files changed, 1242 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webdriver/actions/__init__.py b/testing/web-platform/tests/webdriver/actions/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/__init__.py
diff --git a/testing/web-platform/tests/webdriver/actions/conftest.py b/testing/web-platform/tests/webdriver/actions/conftest.py
new file mode 100644
index 000000000..b51a6d4e0
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/conftest.py
@@ -0,0 +1,34 @@
+import pytest
+
+
+@pytest.fixture
+def key_chain(session):
+ return session.actions.sequence("key", "keyboard_id")
+
+
+@pytest.fixture
+def mouse_chain(session):
+ return session.actions.sequence(
+ "pointer",
+ "pointer_id",
+ {"pointerType": "mouse"})
+
+
+@pytest.fixture(autouse=True)
+def release_actions(session, request):
+ # release all actions after each test
+ # equivalent to a teardown_function, but with access to session fixture
+ request.addfinalizer(session.actions.release)
+
+
+@pytest.fixture
+def key_reporter(session, test_actions_page, request):
+ """Represents focused input element from `test_keys_page` fixture."""
+ input_el = session.find.css("#keys", all=False)
+ input_el.click()
+ return input_el
+
+
+@pytest.fixture
+def test_actions_page(session, server):
+ session.url = server.where_is("webdriver/actions/support/test_actions_wdspec.html")
diff --git a/testing/web-platform/tests/webdriver/actions/key.py b/testing/web-platform/tests/webdriver/actions/key.py
new file mode 100644
index 000000000..d4f313c47
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/key.py
@@ -0,0 +1,170 @@
+import pytest
+
+from support.keys import Keys
+from support.refine import get_keys, filter_dict, get_events
+
+
+def test_lone_keyup_sends_no_events(session, key_reporter, key_chain):
+ key_chain.key_up("a").perform()
+ assert len(get_keys(key_reporter)) == 0
+ assert len(get_events(session)) == 0
+ session.actions.release()
+ assert len(get_keys(key_reporter)) == 0
+ assert len(get_events(session)) == 0
+
+
+# TODO - the harness bails with TIMEOUT before all these subtests complete
+# The timeout is per file, so move to separate file with longer timeout?
+# Need a way to set timeouts in py files (since can't do html meta)
+# @pytest.mark.parametrize("name,expected", ALL_EVENTS.items())
+# def test_webdriver_special_key_sends_keydown(session,
+# key_reporter,
+# key_chain,
+# name,
+# expected):
+# key_chain.key_down(getattr(Keys, name)).perform()
+# # only interested in keydown
+# first_event = get_events(session)[0]
+# # make a copy so we throw out irrelevant keys and compare to events
+# expected = dict(expected)
+# del expected["value"]
+# # check and remove keys that aren't in expected
+# assert first_event["type"] == "keydown"
+# assert first_event["repeat"] == False
+# first_event = filter_dict(first_event, expected)
+# assert first_event == expected
+# # check that printable character was recorded in input field
+# if len(expected["key"]) == 1:
+# assert get_keys(key_reporter) == expected["key"]
+
+
+@pytest.mark.parametrize("value,code", [
+ (u"a", "KeyA",),
+ ("a", "KeyA",),
+ (u"\"", "Quote"),
+ (u",", "Comma"),
+ (u"\u00E0", ""),
+ (u"\u0416", ""),
+ (u"@", "Digit2"),
+ (u"\u2603", ""),
+ (u"\uF6C2", ""), # PUA
+])
+def test_single_printable_key_sends_correct_events(session,
+ key_reporter,
+ key_chain,
+ value,
+ code):
+ key_chain \
+ .key_down(value) \
+ .key_up(value) \
+ .perform()
+ expected = [
+ {"code": code, "key": value, "type": "keydown"},
+ {"code": code, "key": value, "type": "keypress"},
+ {"code": code, "key": value, "type": "keyup"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in get_events(session)]
+ assert events == expected
+ assert get_keys(key_reporter) == value
+
+
+@pytest.mark.parametrize("value", [
+ (u"\U0001F604"),
+ (u"\U0001F60D"),
+])
+def test_single_emoji_records_correct_key(session, key_reporter, key_chain, value):
+ # Not using key_chain.send_keys() because we always want to treat value as
+ # one character here. `len(value)` varies by platform for non-BMP characters,
+ # so we don't want to iterate over value.
+ key_chain \
+ .key_down(value) \
+ .key_up(value) \
+ .perform()
+ # events sent by major browsers are inconsistent so only check key value
+ assert get_keys(key_reporter) == value
+
+
+@pytest.mark.parametrize("value,code,key", [
+ (u"\uE050", "ShiftRight", "Shift"),
+ (u"\uE053", "OSRight", "Meta"),
+ (Keys.CONTROL, "ControlLeft", "Control"),
+])
+def test_single_modifier_key_sends_correct_events(session,
+ key_reporter,
+ key_chain,
+ value,
+ code,
+ key):
+ key_chain \
+ .key_down(value) \
+ .key_up(value) \
+ .perform()
+ all_events = get_events(session)
+ expected = [
+ {"code": code, "key": key, "type": "keydown"},
+ {"code": code, "key": key, "type": "keyup"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in all_events]
+ assert events == expected
+ assert len(get_keys(key_reporter)) == 0
+
+
+@pytest.mark.parametrize("value,code,key", [
+ (Keys.ESCAPE, "Escape", "Escape"),
+ (Keys.RIGHT, "ArrowRight", "ArrowRight"),
+])
+def test_single_nonprintable_key_sends_events(session,
+ key_reporter,
+ key_chain,
+ value,
+ code,
+ key):
+ key_chain \
+ .key_down(value) \
+ .key_up(value) \
+ .perform()
+ expected = [
+ {"code": code, "key": key, "type": "keydown"},
+ {"code": code, "key": key, "type": "keypress"},
+ {"code": code, "key": key, "type": "keyup"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in get_events(session)]
+ if len(events) == 2:
+ # most browsers don't send a keypress for non-printable keys
+ assert events == [expected[0], expected[2]]
+ else:
+ assert events == expected
+ assert len(get_keys(key_reporter)) == 0
+
+
+def test_sequence_of_keydown_printable_keys_sends_events(session,
+ key_reporter,
+ key_chain):
+ key_chain \
+ .key_down("a") \
+ .key_down("b") \
+ .perform()
+ expected = [
+ {"code": "KeyA", "key": "a", "type": "keydown"},
+ {"code": "KeyA", "key": "a", "type": "keypress"},
+ {"code": "KeyB", "key": "b", "type": "keydown"},
+ {"code": "KeyB", "key": "b", "type": "keypress"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in get_events(session)]
+ assert events == expected
+ assert get_keys(key_reporter) == "ab"
+
+
+def test_sequence_of_keydown_character_keys(session, key_reporter, key_chain):
+ key_chain.send_keys("ef").perform()
+ expected = [
+ {"code": "KeyE", "key": "e", "type": "keydown"},
+ {"code": "KeyE", "key": "e", "type": "keypress"},
+ {"code": "KeyE", "key": "e", "type": "keyup"},
+ {"code": "KeyF", "key": "f", "type": "keydown"},
+ {"code": "KeyF", "key": "f", "type": "keypress"},
+ {"code": "KeyF", "key": "f", "type": "keyup"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in get_events(session)]
+ assert events == expected
+ assert get_keys(key_reporter) == "ef"
diff --git a/testing/web-platform/tests/webdriver/actions/mouse.py b/testing/web-platform/tests/webdriver/actions/mouse.py
new file mode 100644
index 000000000..a7192ef3b
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/mouse.py
@@ -0,0 +1,31 @@
+from support.refine import get_events, filter_dict
+
+
+def test_click_at_coordinates(session, test_actions_page, mouse_chain):
+ div_point = {
+ "x": 82,
+ "y": 187,
+ }
+ button = 0
+ mouse_chain \
+ .pointer_move(div_point["x"], div_point["y"], duration=1000) \
+ .pointer_down(button) \
+ .pointer_up(button) \
+ .perform()
+ events = get_events(session)
+ assert len(events) == 4
+ for e in events:
+ if e["type"] != "mousemove":
+ assert e["pageX"] == div_point["x"]
+ assert e["pageY"] == div_point["y"]
+ assert e["target"] == "outer"
+ if e["type"] != "mousedown":
+ assert e["buttons"] == 0
+ assert e["button"] == button
+ expected = [
+ {"type": "mousedown", "buttons": 1},
+ {"type": "mouseup", "buttons": 0},
+ {"type": "click", "buttons": 0},
+ ]
+ filtered_events = [filter_dict(e, expected[0]) for e in events]
+ assert expected == filtered_events[1:]
diff --git a/testing/web-platform/tests/webdriver/actions/sequence.py b/testing/web-platform/tests/webdriver/actions/sequence.py
new file mode 100644
index 000000000..960b800df
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/sequence.py
@@ -0,0 +1,31 @@
+from support.refine import get_keys, filter_dict, get_events
+
+
+def test_no_actions_send_no_events(session, key_reporter, key_chain):
+ key_chain.perform()
+ assert len(get_keys(key_reporter)) == 0
+ assert len(get_events(session)) == 0
+
+
+def test_release_char_sequence_sends_keyup_events_in_reverse(session,
+ key_reporter,
+ key_chain):
+ key_chain \
+ .key_down("a") \
+ .key_down("b") \
+ .perform()
+ # reset so we only see the release events
+ session.execute_script("resetEvents();")
+ session.actions.release()
+ expected = [
+ {"code": "KeyB", "key": "b", "type": "keyup"},
+ {"code": "KeyA", "key": "a", "type": "keyup"},
+ ]
+ events = [filter_dict(e, expected[0]) for e in get_events(session)]
+ assert events == expected
+
+
+def test_release_no_actions_sends_no_events(session, key_reporter):
+ session.actions.release()
+ assert len(get_keys(key_reporter)) == 0
+ assert len(get_events(session)) == 0
diff --git a/testing/web-platform/tests/webdriver/actions/support/__init__.py b/testing/web-platform/tests/webdriver/actions/support/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/support/__init__.py
diff --git a/testing/web-platform/tests/webdriver/actions/support/keys.py b/testing/web-platform/tests/webdriver/actions/support/keys.py
new file mode 100644
index 000000000..842c3db73
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/support/keys.py
@@ -0,0 +1,812 @@
+# Licensed to the Software Freedom Conservancy (SFC) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The SFC licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""
+The Keys implementation.
+"""
+
+from inspect import getmembers
+
+
+class Keys(object):
+ """
+ Set of special keys codes.
+
+ See also https://w3c.github.io/webdriver/webdriver-spec.html#h-keyboard-actions
+ """
+
+ NULL = u"\ue000"
+ CANCEL = u"\ue001" # ^break
+ HELP = u"\ue002"
+ BACKSPACE = u"\ue003"
+ TAB = u"\ue004"
+ CLEAR = u"\ue005"
+ RETURN = u"\ue006"
+ ENTER = u"\ue007"
+ SHIFT = u"\ue008"
+ CONTROL = u"\ue009"
+ ALT = u"\ue00a"
+ PAUSE = u"\ue00b"
+ ESCAPE = u"\ue00c"
+ SPACE = u"\ue00d"
+ PAGE_UP = u"\ue00e"
+ PAGE_DOWN = u"\ue00f"
+ END = u"\ue010"
+ HOME = u"\ue011"
+ LEFT = u"\ue012"
+ UP = u"\ue013"
+ RIGHT = u"\ue014"
+ DOWN = u"\ue015"
+ INSERT = u"\ue016"
+ DELETE = u"\ue017"
+ SEMICOLON = u"\ue018"
+ EQUALS = u"\ue019"
+
+ NUMPAD0 = u"\ue01a" # number pad keys
+ NUMPAD1 = u"\ue01b"
+ NUMPAD2 = u"\ue01c"
+ NUMPAD3 = u"\ue01d"
+ NUMPAD4 = u"\ue01e"
+ NUMPAD5 = u"\ue01f"
+ NUMPAD6 = u"\ue020"
+ NUMPAD7 = u"\ue021"
+ NUMPAD8 = u"\ue022"
+ NUMPAD9 = u"\ue023"
+ MULTIPLY = u"\ue024"
+ ADD = u"\ue025"
+ SEPARATOR = u"\ue026"
+ SUBTRACT = u"\ue027"
+ DECIMAL = u"\ue028"
+ DIVIDE = u"\ue029"
+
+ F1 = u"\ue031" # function keys
+ F2 = u"\ue032"
+ F3 = u"\ue033"
+ F4 = u"\ue034"
+ F5 = u"\ue035"
+ F6 = u"\ue036"
+ F7 = u"\ue037"
+ F8 = u"\ue038"
+ F9 = u"\ue039"
+ F10 = u"\ue03a"
+ F11 = u"\ue03b"
+ F12 = u"\ue03c"
+
+ META = u"\ue03d"
+
+ # More keys from webdriver spec
+ ZENKAKUHANKAKU = u"\uE040"
+ R_SHIFT = u"\uE050"
+ R_CONTROL = u"\uE051"
+ R_ALT = u"\uE052"
+ R_META = u"\uE053"
+ R_PAGEUP = u"\uE054"
+ R_PAGEDOWN = u"\uE055"
+ R_END = u"\uE056"
+ R_HOME = u"\uE057"
+ R_ARROWLEFT = u"\uE058"
+ R_ARROWUP = u"\uE059"
+ R_ARROWRIGHT = u"\uE05A"
+ R_ARROWDOWN = u"\uE05B"
+ R_INSERT = u"\uE05C"
+ R_DELETE = u"\uE05D"
+
+
+ALL_KEYS = getmembers(Keys, lambda x: type(x) == unicode)
+
+ALL_EVENTS = {
+ "ADD": {
+ "code": "",
+ "ctrl": False,
+ "key": "+",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue025",
+ "which": 0,
+ },
+ "ALT": {
+ "code": "AltLeft",
+ "ctrl": False,
+ "key": "Alt",
+ "location": 1,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00a",
+ "which": 0,
+ },
+ "BACKSPACE": {
+ "code": "Backspace",
+ "ctrl": False,
+ "key": "Backspace",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue003",
+ "which": 0,
+ },
+ "CANCEL": {
+ "code": "",
+ "ctrl": False,
+ "key": "Cancel",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue001",
+ "which": 0,
+ },
+ "CLEAR": {
+ "code": "",
+ "ctrl": False,
+ "key": "Clear",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue005",
+ "which": 0,
+ },
+ "CONTROL": {
+ "code": "ControlLeft",
+ "ctrl": True,
+ "key": "Control",
+ "location": 1,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue009",
+ "which": 0,
+ },
+ "DECIMAL": {
+ "code": "NumpadDecimal",
+ "ctrl": False,
+ "key": ".",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue028",
+ "which": 0,
+ },
+ "DELETE": {
+ "code": "Delete",
+ "ctrl": False,
+ "key": "Delete",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue017",
+ "which": 0,
+ },
+ "DIVIDE": {
+ "code": "NumpadDivide",
+ "ctrl": False,
+ "key": "/",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue029",
+ "which": 0,
+ },
+ "DOWN": {
+ "code": "ArrowDown",
+ "ctrl": False,
+ "key": "ArrowDown",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue015",
+ "which": 0,
+ },
+ "END": {
+ "code": "End",
+ "ctrl": False,
+ "key": "End",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue010",
+ "which": 0,
+ },
+ "ENTER": {
+ "code": "NumpadEnter",
+ "ctrl": False,
+ "key": "Enter",
+ "location": 1,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue007",
+ "which": 0,
+ },
+ "EQUALS": {
+ "code": "",
+ "ctrl": False,
+ "key": "=",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue019",
+ "which": 0,
+ },
+ "ESCAPE": {
+ "code": "Escape",
+ "ctrl": False,
+ "key": "Escape",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00c",
+ "which": 0,
+ },
+ "F1": {
+ "code": "F1",
+ "ctrl": False,
+ "key": "F1",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue031",
+ "which": 0,
+ },
+ "F10": {
+ "code": "F10",
+ "ctrl": False,
+ "key": "F10",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue03a",
+ "which": 0,
+ },
+ "F11": {
+ "code": "F11",
+ "ctrl": False,
+ "key": "F11",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue03b",
+ "which": 0,
+ },
+ "F12": {
+ "code": "F12",
+ "ctrl": False,
+ "key": "F12",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue03c",
+ "which": 0,
+ },
+ "F2": {
+ "code": "F2",
+ "ctrl": False,
+ "key": "F2",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue032",
+ "which": 0,
+ },
+ "F3": {
+ "code": "F3",
+ "ctrl": False,
+ "key": "F3",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue033",
+ "which": 0,
+ },
+ "F4": {
+ "code": "F4",
+ "ctrl": False,
+ "key": "F4",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue034",
+ "which": 0,
+ },
+ "F5": {
+ "code": "F5",
+ "ctrl": False,
+ "key": "F5",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue035",
+ "which": 0,
+ },
+ "F6": {
+ "code": "F6",
+ "ctrl": False,
+ "key": "F6",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue036",
+ "which": 0,
+ },
+ "F7": {
+ "code": "F7",
+ "ctrl": False,
+ "key": "F7",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue037",
+ "which": 0,
+ },
+ "F8": {
+ "code": "F8",
+ "ctrl": False,
+ "key": "F8",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue038",
+ "which": 0,
+ },
+ "F9": {
+ "code": "F9",
+ "ctrl": False,
+ "key": "F9",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue039",
+ "which": 0,
+ },
+ "HELP": {
+ "code": "Help",
+ "ctrl": False,
+ "key": "Help",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue002",
+ "which": 0,
+ },
+ "HOME": {
+ "code": "Home",
+ "ctrl": False,
+ "key": "Home",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue011",
+ "which": 0,
+ },
+ "INSERT": {
+ "code": "Insert",
+ "ctrl": False,
+ "key": "Insert",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue016",
+ "which": 0,
+ },
+ "LEFT": {
+ "code": "ArrowLeft",
+ "ctrl": False,
+ "key": "ArrowLeft",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue012",
+ "which": 0,
+ },
+ "META": {
+ "code": "OSLeft",
+ "ctrl": False,
+ "key": "Meta",
+ "location": 1,
+ "meta": True,
+ "shift": False,
+ "value": u"\ue03d",
+ "which": 0,
+ },
+ "MULTIPLY": {
+ "code": "NumpadMultiply",
+ "ctrl": False,
+ "key": "*",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue024",
+ "which": 0,
+ },
+ "NULL": {
+ "code": "",
+ "ctrl": False,
+ "key": "Unidentified",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue000",
+ "which": 0,
+ },
+ "NUMPAD0": {
+ "code": "Numpad0",
+ "ctrl": False,
+ "key": "0",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01a",
+ "which": 0,
+ },
+ "NUMPAD1": {
+ "code": "Numpad1",
+ "ctrl": False,
+ "key": "1",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01b",
+ "which": 0,
+ },
+ "NUMPAD2": {
+ "code": "Numpad2",
+ "ctrl": False,
+ "key": "2",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01c",
+ "which": 0,
+ },
+ "NUMPAD3": {
+ "code": "Numpad3",
+ "ctrl": False,
+ "key": "3",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01d",
+ "which": 0,
+ },
+ "NUMPAD4": {
+ "code": "PageDown",
+ "ctrl": False,
+ "key": "4",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01e",
+ "which": 0,
+ },
+ "NUMPAD5": {
+ "code": "PageUp",
+ "ctrl": False,
+ "key": "5",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue01f",
+ "which": 0,
+ },
+ "NUMPAD6": {
+ "code": "Numpad6",
+ "ctrl": False,
+ "key": "6",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue020",
+ "which": 0,
+ },
+ "NUMPAD7": {
+ "code": "Numpad7",
+ "ctrl": False,
+ "key": "7",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue021",
+ "which": 0,
+ },
+ "NUMPAD8": {
+ "code": "Numpad8",
+ "ctrl": False,
+ "key": "8",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue022",
+ "which": 0,
+ },
+ "NUMPAD9": {
+ "code": "Numpad9",
+ "ctrl": False,
+ "key": "9",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue023",
+ "which": 0,
+ },
+ "PAGE_DOWN": {
+ "code": "",
+ "ctrl": False,
+ "key": "PageDown",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00f",
+ "which": 0,
+ },
+ "PAGE_UP": {
+ "code": "",
+ "ctrl": False,
+ "key": "PageUp",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00e",
+ "which": 0,
+ },
+ "PAUSE": {
+ "code": "",
+ "ctrl": False,
+ "key": "Pause",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00b",
+ "which": 0,
+ },
+ "RETURN": {
+ "code": "Enter",
+ "ctrl": False,
+ "key": "Return",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue006",
+ "which": 0,
+ },
+ "RIGHT": {
+ "code": "ArrowRight",
+ "ctrl": False,
+ "key": "ArrowRight",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue014",
+ "which": 0,
+ },
+ "R_ALT": {
+ "code": "AltRight",
+ "ctrl": False,
+ "key": "Alt",
+ "location": 2,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue052",
+ "which": 0,
+ },
+ "R_ARROWDOWN": {
+ "code": "Numpad2",
+ "ctrl": False,
+ "key": "ArrowDown",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue05b",
+ "which": 0,
+ },
+ "R_ARROWLEFT": {
+ "code": "Numpad4",
+ "ctrl": False,
+ "key": "ArrowLeft",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue058",
+ "which": 0,
+ },
+ "R_ARROWRIGHT": {
+ "code": "Numpad6",
+ "ctrl": False,
+ "key": "ArrowRight",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue05a",
+ "which": 0,
+ },
+ "R_ARROWUP": {
+ "code": "Numpad8",
+ "ctrl": False,
+ "key": "ArrowUp",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue059",
+ "which": 0,
+ },
+ "R_CONTROL": {
+ "code": "ControlRight",
+ "ctrl": True,
+ "key": "Control",
+ "location": 2,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue051",
+ "which": 0,
+ },
+ "R_DELETE": {
+ "code": "NumpadDecimal",
+ "ctrl": False,
+ "key": "Delete",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue05d",
+ "which": 0,
+ },
+ "R_END": {
+ "code": "Numpad1",
+ "ctrl": False,
+ "key": "End",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue056",
+ "which": 0,
+ },
+ "R_HOME": {
+ "code": "Numpad7",
+ "ctrl": False,
+ "key": "Home",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue057",
+ "which": 0,
+ },
+ "R_INSERT": {
+ "code": "Numpad0",
+ "ctrl": False,
+ "key": "Insert",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue05c",
+ "which": 0,
+ },
+ "R_META": {
+ "code": "OSRight",
+ "ctrl": False,
+ "key": "Meta",
+ "location": 2,
+ "meta": True,
+ "shift": False,
+ "value": u"\ue053",
+ "which": 0,
+ },
+ "R_PAGEDOWN": {
+ "code": "Numpad3",
+ "ctrl": False,
+ "key": "PageDown",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue055",
+ "which": 0,
+ },
+ "R_PAGEUP": {
+ "code": "Numpad9",
+ "ctrl": False,
+ "key": "PageUp",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue054",
+ "which": 0,
+ },
+ "R_SHIFT": {
+ "code": "ShiftRight",
+ "ctrl": False,
+ "key": "Shift",
+ "location": 2,
+ "meta": False,
+ "shift": True,
+ "value": u"\ue050",
+ "which": 0,
+ },
+ "SEMICOLON": {
+ "code": "",
+ "ctrl": False,
+ "key": ";",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue018",
+ "which": 0,
+ },
+ "SEPARATOR": {
+ "code": "NumpadSubtract",
+ "ctrl": False,
+ "key": ",",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue026",
+ "which": 0,
+ },
+ "SHIFT": {
+ "code": "ShiftLeft",
+ "ctrl": False,
+ "key": "Shift",
+ "location": 1,
+ "meta": False,
+ "shift": True,
+ "value": u"\ue008",
+ "which": 0,
+ },
+ "SPACE": {
+ "code": "Space",
+ "ctrl": False,
+ "key": " ",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue00d",
+ "which": 0,
+ },
+ "SUBTRACT": {
+ "code": "",
+ "ctrl": False,
+ "key": "-",
+ "location": 3,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue027",
+ "which": 0,
+ },
+ "TAB": {
+ "code": "Tab",
+ "ctrl": False,
+ "key": "Tab",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue004",
+ "which": 0,
+ },
+ "UP": {
+ "code": "ArrowUp",
+ "ctrl": False,
+ "key": "ArrowUp",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue013",
+ "which": 0,
+ },
+ "ZENKAKUHANKAKU": {
+ "code": "",
+ "ctrl": False,
+ "key": "ZenkakuHankaku",
+ "location": 0,
+ "meta": False,
+ "shift": False,
+ "value": u"\ue040",
+ "which": 0,
+ }
+}
diff --git a/testing/web-platform/tests/webdriver/actions/support/refine.py b/testing/web-platform/tests/webdriver/actions/support/refine.py
new file mode 100644
index 000000000..3a6d63e04
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/support/refine.py
@@ -0,0 +1,33 @@
+def get_events(session):
+ """Return list of key events recorded in the test_keys_page fixture."""
+ events = session.execute_script("return allEvents.events;") or []
+ # `key` values in `allEvents` may be escaped (see `escapeSurrogateHalf` in
+ # test_keys_wdspec.html), so this converts them back into unicode literals.
+ for e in events:
+ # example: turn "U+d83d" (6 chars) into u"\ud83d" (1 char)
+ if "key" in e and e["key"].startswith(u"U+"):
+ key = e["key"]
+ hex_suffix = key[key.index("+") + 1:]
+ e["key"] = unichr(int(hex_suffix, 16))
+ return events
+
+
+def get_keys(input_el):
+ """Get printable characters entered into `input_el`.
+
+ :param input_el: HTML input element.
+ """
+ rv = input_el.property("value")
+ if rv is None:
+ return ""
+ else:
+ return rv
+
+
+def filter_dict(source, d):
+ """Filter `source` dict to only contain same keys as `d` dict.
+
+ :param source: dictionary to filter.
+ :param d: dictionary whose keys determine the filtering.
+ """
+ return {k: source[k] for k in d.keys()}
diff --git a/testing/web-platform/tests/webdriver/actions/support/test_actions_wdspec.html b/testing/web-platform/tests/webdriver/actions/support/test_actions_wdspec.html
new file mode 100644
index 000000000..38b3885f6
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/actions/support/test_actions_wdspec.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+ <title>Test Actions</title>
+ <style>
+ div { padding:0px; margin: 0px; }
+ #resultContainer { width: 600px; height: 60px; }
+ #outer { width: 100px; height: 50px; background-color: #ccc; }
+ #trackPointer {
+ width: 5px;
+ height: 5px;
+ border: solid 1px red;
+ position: fixed; }
+ </style>
+ <script>
+ var allEvents = {events: []};
+ function displayMessage(message) {
+ document.getElementById("events").innerHTML = "<p>" + message + "</p>";
+ }
+
+ function appendMessage(message) {
+ document.getElementById("events").innerHTML += "<p>" + message + "</p>";
+ }
+
+ /**
+ * Escape |key| if it's in a surrogate-half character range.
+ *
+ * Example: given "\ud83d" return "U+d83d".
+ *
+ * Otherwise JSON.stringify will convert it to U+FFFD (REPLACEMENT CHARACTER)
+ * when returning a value from executeScript, for example.
+ */
+ function escapeSurrogateHalf(key) {
+ if (typeof key !== "undefined" && key.length === 1) {
+ var charCode = key.charCodeAt(0);
+ var highSurrogate = charCode >= 0xD800 && charCode <= 0xDBFF;
+ var surrogate = highSurrogate || (charCode >= 0xDC00 && charCode <= 0xDFFF);
+ if (surrogate) {
+ key = "U+" + charCode.toString(16);
+ }
+ }
+ return key;
+ }
+
+ function recordKeyboardEvent(event) {
+ var key = escapeSurrogateHalf(event.key);
+ allEvents.events.push({
+ "code": event.code,
+ "key": key,
+ "which": event.which,
+ "location": event.location,
+ "ctrl": event.ctrlKey,
+ "meta": event.metaKey,
+ "shift": event.shiftKey,
+ "repeat": event.repeat,
+ "type": event.type
+ });
+ appendMessage(`${event.type}(` +
+ `code: ${event.code}, ` +
+ `key: ${key}, ` +
+ `which: ${event.which})`);
+ }
+
+ function recordPointerEvent(event) {
+ allEvents.events.push({
+ "type": event.type,
+ "button": event.button,
+ "buttons": event.buttons,
+ "pageX": event.pageX,
+ "pageY": event.pageY,
+ "target": event.target.id
+ });
+ appendMessage(`${event.type}(` +
+ `button: ${event.button}, ` +
+ `pageX: ${event.pageX}, ` +
+ `pageY: ${event.pageY}, ` +
+ `button: ${event.button}, ` +
+ `buttons: ${event.buttons}, ` +
+ `target id: ${event.target.id})`);
+ }
+
+ function recordFirstPointerMove(event) {
+ recordPointerEvent(event);
+ window.removeEventListener("mousemove", recordFirstPointerMove);
+ }
+
+ function resetEvents() {
+ allEvents.events.length = 0;
+ displayMessage("");
+ }
+
+ document.addEventListener("DOMContentLoaded", () => {
+ var keyReporter = document.getElementById("keys");
+ ["keyup", "keypress", "keydown"].forEach((e) => {
+ keyReporter.addEventListener(e, recordKeyboardEvent);
+ });
+ var outer = document.getElementById("outer");
+ ["click", "dblclick", "mousedown",
+ "mouseup", "contextmenu"].forEach((e) => {
+ outer.addEventListener(e, recordPointerEvent);
+ });
+ window.addEventListener("mousemove", recordFirstPointerMove);
+ //visual cue for mousemove
+ var pointer = document.getElementById("trackPointer");
+ window.addEventListener("mousemove", (e) => {
+ setTimeout(() => {
+ let offset = 15;
+ pointer.style.top = e.pageY + offset + "px";
+ pointer.style.left = e.pageX + offset + "px";
+ }, 30);
+ });
+ });
+ </script>
+</head>
+<body>
+ <div id="trackPointer"></div>
+ <div>
+ <h2>KeyReporter</h2>
+ <input type="text" id="keys" size="80">
+ </div>
+ <div>
+ <h2>ClickReporter</h2>
+ <div id="outer">
+ </div>
+ </div>
+ <div id="resultContainer">
+ <h2>Events</h2>
+ <div id="events"></div>
+ </div>
+</body>
+</html>