diff --git a/tools/webdriver/webdriver/__init__.py b/tools/webdriver/webdriver/__init__.py index d860b9ea2533f0..0e1a9de9a65efb 100644 --- a/tools/webdriver/webdriver/__init__.py +++ b/tools/webdriver/webdriver/__init__.py @@ -6,6 +6,7 @@ Find, Frame, Session, + ShadowRoot, Timeouts, Window) from .error import ( diff --git a/tools/webdriver/webdriver/client.py b/tools/webdriver/webdriver/client.py index fc4222a9ce9598..19fe336a6e1eb4 100644 --- a/tools/webdriver/webdriver/client.py +++ b/tools/webdriver/webdriver/client.py @@ -372,6 +372,42 @@ def from_json(cls, json, session): return cls(uuid, session) +class ShadowRoot(object): + identifier = "shadow-075b-4da1-b6ba-e579c2d3230a" + + def __init__(self, session, id): + """ + Construct a new shadow root representation. + + :param id: Shadow root UUID which must be unique across + all browsing contexts. + :param session: Current ``webdriver.Session``. + """ + self.id = id + self.session = session + + @classmethod + def from_json(cls, json, session): + uuid = json[ShadowRoot.identifier] + return cls(uuid, session) + + def send_shadow_command(self, method, uri, body=None): + url = "shadow/{}/{}".format(self.id, uri) + return self.session.send_session_command(method, url, body) + + @command + def find_element(self, strategy, selector): + body = {"using": strategy, + "value": selector} + return self.send_shadow_command("POST", "element", body) + + @command + def find_elements(self, strategy, selector): + body = {"using": strategy, + "value": selector} + return self.send_shadow_command("POST", "elements", body) + + class Find(object): def __init__(self, session): self.session = session @@ -804,6 +840,11 @@ def selected(self): def screenshot(self): return self.send_element_command("GET", "screenshot") + @property + @command + def shadow_root(self): + return self.send_element_command("GET", "shadow") + @command def attribute(self, name): return self.send_element_command("GET", "attribute/%s" % name) diff --git a/tools/webdriver/webdriver/error.py b/tools/webdriver/webdriver/error.py index 8379656d73dd72..642612985a8414 100644 --- a/tools/webdriver/webdriver/error.py +++ b/tools/webdriver/webdriver/error.py @@ -34,6 +34,11 @@ def __str__(self): return message +class DetachedShadowRootException(WebDriverException): + http_status = 404 + status_code = "detached shadow root" + + class ElementClickInterceptedException(WebDriverException): http_status = 400 status_code = "element click intercepted" @@ -114,6 +119,11 @@ class NoSuchFrameException(WebDriverException): status_code = "no such frame" +class NoSuchShadowRootException(WebDriverException): + http_status = 404 + status_code = "no such shadow root" + + class NoSuchWindowException(WebDriverException): http_status = 404 status_code = "no such window" diff --git a/tools/webdriver/webdriver/protocol.py b/tools/webdriver/webdriver/protocol.py index c811f6f26db790..d3faa8508b0e1c 100644 --- a/tools/webdriver/webdriver/protocol.py +++ b/tools/webdriver/webdriver/protocol.py @@ -22,6 +22,8 @@ def default(self, obj): return {webdriver.Frame.identifier: obj.id} elif isinstance(obj, webdriver.Window): return {webdriver.Frame.identifier: obj.id} + elif isinstance(obj, webdriver.ShadowRoot): + return {webdriver.ShadowRoot.identifier: obj.id} return super(Encoder, self).default(obj) @@ -40,6 +42,8 @@ def object_hook(self, payload): return webdriver.Frame.from_json(payload, self.session) elif isinstance(payload, dict) and webdriver.Window.identifier in payload: return webdriver.Window.from_json(payload, self.session) + elif isinstance(payload, dict) and webdriver.ShadowRoot.identifier in payload: + return webdriver.ShadowRoot.from_json(payload, self.session) elif isinstance(payload, dict): return {k: self.object_hook(v) for k, v in iteritems(payload)} return payload diff --git a/webdriver/tests/find_element_from_shadow_root/conftest.py b/webdriver/tests/find_element_from_shadow_root/conftest.py new file mode 100644 index 00000000000000..b8fc93319f43a9 --- /dev/null +++ b/webdriver/tests/find_element_from_shadow_root/conftest.py @@ -0,0 +1,19 @@ +import pytest + +@pytest.fixture +def get_shadow_page(inline): + def get_shadow_page(shadow_content): + return inline(""" + + """.format(shadow_content)) + return get_shadow_page diff --git a/webdriver/tests/find_element_from_shadow_root/find.py b/webdriver/tests/find_element_from_shadow_root/find.py new file mode 100644 index 00000000000000..87941d495e1355 --- /dev/null +++ b/webdriver/tests/find_element_from_shadow_root/find.py @@ -0,0 +1,141 @@ +import pytest + +from webdriver.transport import Response + +from tests.support.asserts import assert_error, assert_same_element, assert_success + + +def find_element(session, shadow_id, using, value): + return session.transport.send( + "POST", "session/{session_id}/shadow/{shadow_id}/element".format( + session_id=session.session_id, + shadow_id=shadow_id), + {"using": using, "value": value}) + + +def test_null_parameter_value(session, http, get_shadow_page): + session.url = get_shadow_page("
full link text
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + + path = "/session/{session_id}/shadow/{shadow_id}/element".format( + session_id=session.session_id, shadow_id=shadow_root.id) + with http.post(path, None) as response: + assert_error(Response.from_http(response), "invalid argument") + + +def test_no_top_browsing_context(session, closed_window): + response = find_element(session, "notReal", "css selector", "foo") + assert_error(response, "no such window") + + +def test_no_browsing_context(session, closed_frame): + response = find_element(session, "notReal", "css selector", "foo") + assert_error(response, "no such window") + + +@pytest.mark.parametrize("using", ["a", True, None, 1, [], {}]) +def test_invalid_using_argument(session, using): + # Step 1 - 2 + response = find_element(session, "notReal", using, "value") + assert_error(response, "invalid argument") + + +@pytest.mark.parametrize("value", [None, [], {}]) +def test_invalid_selector_argument(session, value): + # Step 3 - 4 + response = find_element(session, "notReal", "css selector", value) + assert_error(response, "invalid argument") + + +def test_detached_shadow_root(session, get_shadow_page): + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + session.refresh() + + response = find_element(session, shadow_root.id, "css", "input") + assert_error(response, "detached shadow root") + + +def test_found_element_equivalence(session, get_shadow_page): + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + expected = session.execute_script("return arguments[0].shadowRoot.querySelector('input')", + args=(custom_element,)) + shadow_root = custom_element.shadow_root + response = find_element(session, shadow_root.id, "css", "input") + value = assert_success(response) + assert_same_element(session, value, expected) + + +@pytest.mark.parametrize("using,value", + [("css selector", "#linkText"), + ("link text", "full link text"), + ("partial link text", "link text"), + ("tag name", "a"), + ("xpath", "//a")]) +def test_find_element(session, get_shadow_page, using, value): + # Step 8 - 9 + session.url = get_shadow_page("
full link text
") + custom_element = session.find.css("custom-shadow-element", all=False) + expected = session.execute_script("return arguments[0].shadowRoot.querySelector('#linkText')", + args=(custom_element,)) + shadow_root = custom_element.shadow_root + response = find_element(session, shadow_root.id, using, value) + assert_success(response) + assert_same_element(session, value, expected) + + +@pytest.mark.parametrize("document,value", [ + ("link text", "link text"), + (" link text ", "link text"), + ("link
text
", "link\ntext"), + ("link&text", "link&text"), + ("LINK TEXT", "LINK TEXT"), + ("link text", "LINK TEXT"), +]) +def test_find_element_link_text(session, get_shadow_page, document, value): + # Step 8 - 9 + session.url = get_shadow_page("
{0}
".format(document)) + custom_element = session.find.css("custom-shadow-element", all=False) + expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[0]", + args=(custom_element,)) + shadow_root = custom_element.shadow_root + + response = find_element(session, shadow_root.id, "link text", value) + assert_success(response) + assert_same_element(session, value, expected) + + +@pytest.mark.parametrize("document,value", [ + ("partial link text", "link"), + (" partial link text ", "link"), + ("partial link text", "k t"), + ("partial link
text
", "k\nt"), + ("partial link&text", "k&t"), + ("PARTIAL LINK TEXT", "LINK"), + ("partial link text", "LINK"), +]) +def test_find_element_partial_link_text(session, get_shadow_page, document, value): + # Step 8 - 9 + session.url = get_shadow_page("
{0}
".format(document)) + custom_element = session.find.css("custom-shadow-element", all=False) + expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[0]", + args=(custom_element,)) + shadow_root = custom_element.shadow_root + + response = find_element(session, shadow_root.id, "partial link text", value) + assert_success(response) + assert_same_element(session, value, expected) + + +@pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) +def test_no_element(session, get_shadow_page, using, value): + # Step 8 - 9 + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + + response = find_element(session, shadow_root.id, using, value) + assert_error(response, "no such element") diff --git a/webdriver/tests/find_element_from_shadow_root/user_prompts.py b/webdriver/tests/find_element_from_shadow_root/user_prompts.py new file mode 100644 index 00000000000000..3813a7df5d3242 --- /dev/null +++ b/webdriver/tests/find_element_from_shadow_root/user_prompts.py @@ -0,0 +1,129 @@ +# META: timeout=long + +import pytest + +from tests.support.asserts import ( + assert_error, + assert_same_element, + assert_success, + assert_dialog_handled, +) + + +def find_element(session, shadow_id, using, value): + return session.transport.send( + "POST", "session/{session_id}/shadow/{shadow_id}/element".format( + session_id=session.session_id, + element_id=shadow_id), + {"using": using, "value": value}) + + +@pytest.fixture +def check_user_prompt_closed_without_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_closed_without_exception(dialog_type, retval): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + inner_element = session.execute_script("return arguments[0].shadowRoot.querySelector('p')", + args=(outer_element,)) + + create_dialog(dialog_type, text=dialog_type) + + response = find_element(session, shadow_root.id, "css selector", "p") + value = assert_success(response) + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + assert_same_element(session, value, inner_element) + + return check_user_prompt_closed_without_exception + + +@pytest.fixture +def check_user_prompt_closed_with_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_closed_with_exception(dialog_type, retval): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + + create_dialog(dialog_type, text=dialog_type) + + response = find_element(session, shadow_root.id, "css selector", "p") + assert_error(response, "unexpected alert open") + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + return check_user_prompt_closed_with_exception + + +@pytest.fixture +def check_user_prompt_not_closed_but_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_not_closed_but_exception(dialog_type): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + + create_dialog(dialog_type, text=dialog_type) + + response = find_element(session, shadow_root.id, "css selector", "p") + assert_error(response, "unexpected alert open") + + assert session.alert.text == dialog_type + session.alert.dismiss() + + return check_user_prompt_not_closed_but_exception + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): + check_user_prompt_not_closed_but_exception(dialog_type) + + +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) diff --git a/webdriver/tests/find_elements_from_shadow_root/conftest.py b/webdriver/tests/find_elements_from_shadow_root/conftest.py new file mode 100644 index 00000000000000..b8fc93319f43a9 --- /dev/null +++ b/webdriver/tests/find_elements_from_shadow_root/conftest.py @@ -0,0 +1,19 @@ +import pytest + +@pytest.fixture +def get_shadow_page(inline): + def get_shadow_page(shadow_content): + return inline(""" + + """.format(shadow_content)) + return get_shadow_page diff --git a/webdriver/tests/find_elements_from_shadow_root/find.py b/webdriver/tests/find_elements_from_shadow_root/find.py new file mode 100644 index 00000000000000..f70a4b45aadb05 --- /dev/null +++ b/webdriver/tests/find_elements_from_shadow_root/find.py @@ -0,0 +1,144 @@ +import pytest + +from webdriver.transport import Response + +from tests.support.asserts import assert_error, assert_same_element, assert_success + + +def find_elements(session, shadow_id, using, value): + return session.transport.send( + "POST", "session/{session_id}/shadow/{shadow_id}/elements".format( + session_id=session.session_id, + shadow_id=shadow_id), + {"using": using, "value": value}) + + +def test_null_parameter_value(session, http, inline): + session.url = inline("") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + + path = "/session/{session_id}/shadow/{shadow_id}/elements".format( + session_id=session.session_id, shadow_id=shadow_root.id) + with http.post(path, None) as response: + assert_error(Response.from_http(response), "invalid argument") + + +def test_no_top_browsing_context(session, closed_window): + response = find_elements(session, "notReal", "css selector", "foo") + assert_error(response, "no such window") + + +def test_no_browsing_context(session, closed_frame): + response = find_elements(session, "notReal", "css selector", "foo") + assert_error(response, "no such window") + + +@pytest.mark.parametrize("using", [("a"), (True), (None), (1), ([]), ({})]) +def test_invalid_using_argument(session, using): + # Step 1 - 2 + response = find_elements(session, "notReal", using, "value") + assert_error(response, "invalid argument") + + +@pytest.mark.parametrize("value", [None, [], {}]) +def test_invalid_selector_argument(session, value): + # Step 3 - 4 + response = find_elements(session, "notReal", "css selector", value) + assert_error(response, "invalid argument") + + +def test_detached_shadow_root(session, get_shadow_page): + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + session.refresh() + + response = find_elements(session, shadow_root.id, "css", "input") + assert_error(response, "detached shadow root") + + +def test_find_elements_equivalence(session, get_shadow_page): + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + response = find_elements(session, shadow_root.id, "css", "input") + assert_success(response) + + +@pytest.mark.parametrize("using,value", + [("css selector", "#linkText"), + ("link text", "full link text"), + ("partial link text", "link text"), + ("tag name", "a"), + ("xpath", "//a")]) +def test_find_elements(session, get_shadow_page, using, value): + # Step 8 - 9 + session.url = get_shadow_page("") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + response = find_elements(session, shadow_root.id, using, value) + assert_success(response) + + +@pytest.mark.parametrize("document,value", [ + ("link text", "link text"), + (" link text ", "link text"), + ("link
text
", "link\ntext"), + ("link&text", "link&text"), + ("LINK TEXT", "LINK TEXT"), + ("link text", "LINK TEXT"), +]) +def test_find_elements_link_text(session, get_shadow_page, document, value): + # Step 8 - 9 + session.url = get_shadow_page("".format(document)) + element = session.find.css("div", all=False) + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[1]", + args=(custom_element,)) + + response = find_elements(session, shadow_root.id, "link text", value) + value = assert_success(response) + assert isinstance(value, list) + assert len(value) == 1 + + found_element = value[0] + assert_same_element(session, found_element, expected) + + +@pytest.mark.parametrize("document,value", [ + ("partial link text", "link"), + (" partial link text ", "link"), + ("partial link text", "k t"), + ("partial link
text
", "k\nt"), + ("partial link&text", "k&t"), + ("PARTIAL LINK TEXT", "LINK"), + ("partial link text", "LINK"), +]) +def test_find_elements_partial_link_text(session, get_shadow_page, document, value): + # Step 8 - 9 + session.url = get_shadow_page("".format(document)) + element = session.find.css("div", all=False) + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[1]", + args=(custom_element,)) + + response = find_elements(session, shadow_root.id, "partial link text", value) + value = assert_success(response) + assert isinstance(value, list) + assert len(value) == 1 + + found_element = value[0] + assert_same_element(session, found_element, expected) + + +@pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) +def test_no_element(session, get_shadow_page, using, value): + # Step 8 - 9 + session.url = get_shadow_page("
") + custom_element = session.find.css("custom-shadow-element", all=False) + shadow_root = custom_element.shadow_root + response = find_elements(session, shadow_root.id, using, value) + assert response.body["value"] == [] diff --git a/webdriver/tests/find_elements_from_shadow_root/user_prompts.py b/webdriver/tests/find_elements_from_shadow_root/user_prompts.py new file mode 100644 index 00000000000000..c4b903c836ef8e --- /dev/null +++ b/webdriver/tests/find_elements_from_shadow_root/user_prompts.py @@ -0,0 +1,131 @@ +# META: timeout=long + +import pytest + +from tests.support.asserts import ( + assert_error, + assert_same_element, + assert_success, + assert_dialog_handled, +) + + +def find_elements(session, shadow_id, using, value): + return session.transport.send( + "POST", "session/{session_id}/shadow/{shadow_id}/elements".format( + session_id=session.session_id, + element_id=shadow_id), + {"using": using, "value": value}) + + +@pytest.fixture +def check_user_prompt_closed_without_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_closed_without_exception(dialog_type, retval): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + inner_element = session.execute_script("return arguments[0].shadowRoot.querySelector('p')", + args=(outer_element,)) + + create_dialog(dialog_type, text=dialog_type) + + response = find_elements(session, shadow_root.id, "css selector", "p") + value = assert_success(response) + assert isinstance(value, list) + assert len(value) == 1 + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + assert_same_element(session, value[0], inner_element) + + return check_user_prompt_closed_without_exception + + +@pytest.fixture +def check_user_prompt_closed_with_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_closed_with_exception(dialog_type, retval): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + + create_dialog(dialog_type, text=dialog_type) + + response = find_elements(session, shadow_root.id, "css selector", "p") + assert_error(response, "unexpected alert open") + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + return check_user_prompt_closed_with_exception + + +@pytest.fixture +def check_user_prompt_not_closed_but_exception(session, create_dialog, get_shadow_page): + def check_user_prompt_not_closed_but_exception(dialog_type): + session.url = get_shadow_page("

bar

") + outer_element = session.find.css("custom-shadow-element", all=False) + shadow_root = outer_element.shadow_root + + create_dialog(dialog_type, text=dialog_type) + + response = find_elements(session, shadow_root.id, "css selector", "p") + assert_error(response, "unexpected alert open") + + assert session.alert.text == dialog_type + session.alert.dismiss() + + return check_user_prompt_not_closed_but_exception + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): + check_user_prompt_not_closed_but_exception(dialog_type) + + +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) diff --git a/webdriver/tests/get_element_shadow_root/conftest.py b/webdriver/tests/get_element_shadow_root/conftest.py new file mode 100644 index 00000000000000..afc0c91c3d3f2f --- /dev/null +++ b/webdriver/tests/get_element_shadow_root/conftest.py @@ -0,0 +1,22 @@ +import pytest + +@pytest.fixture +def checkbox_dom(inline): + return inline(""" + + + """) diff --git a/webdriver/tests/get_element_shadow_root/get.py b/webdriver/tests/get_element_shadow_root/get.py new file mode 100644 index 00000000000000..27c1e4a44ee07a --- /dev/null +++ b/webdriver/tests/get_element_shadow_root/get.py @@ -0,0 +1,58 @@ +import pytest + +from tests.support.asserts import assert_error, assert_same_element, assert_success + + +def get_shadow_root(session, element_id): + return session.transport.send( + "GET", "session/{session_id}/element/{element_id}/shadow".format( + session_id=session.session_id, + element_id=element_id)) + + +def test_no_top_browsing_context(session, closed_window): + original_handle, element = closed_window + response = get_shadow_root(session, element.id) + assert_error(response, "no such window") + response = get_shadow_root(session, "foo") + assert_error(response, "no such window") + + session.window_handle = original_handle + response = get_shadow_root(session, element.id) + assert_error(response, "no such element") + + +def test_no_browsing_context(session, closed_frame): + response = get_shadow_root(session, "foo") + assert_error(response, "no such window") + + +def test_element_not_found(session): + result = get_shadow_root(session, "foo") + assert_error(result, "no such element") + + +def test_element_stale(session, checkbox_dom): + session.url = checkbox_dom + element = session.find.css("custom-checkbox-element", all=False) + session.refresh() + + result = get_shadow_root(session, element.id) + assert_error(result, "stale element reference") + + +def test_get_shadow_root(session, checkbox_dom): + session.url = checkbox_dom + expected = session.execute_script( + "return document.querySelector('custom-checkbox-element').shadowRoot.host") + custom_element = session.find.css("custom-checkbox-element", all=False) + response = get_shadow_root(session, custom_element.id) + assert_success(response) + assert_same_element(session, custom_element, expected) + + +def test_no_shadow_root(session, inline): + session.url = inline("

no shadow root

") + element = session.find.css("div", all=False) + response = get_shadow_root(session, element.id) + assert_error(response, "no such shadow root") diff --git a/webdriver/tests/get_element_shadow_root/user_prompts.py b/webdriver/tests/get_element_shadow_root/user_prompts.py new file mode 100644 index 00000000000000..55b50f0d9c937b --- /dev/null +++ b/webdriver/tests/get_element_shadow_root/user_prompts.py @@ -0,0 +1,118 @@ +# META: timeout=long + +import pytest + +from tests.support.asserts import assert_error, assert_success, assert_dialog_handled + + +def get_shadow_root(session, element_id): + return session.transport.send( + "GET", "session/{session_id}/element/{element_id}/shadow".format( + session_id=session.session_id, + element_id=element_id)) + + +@pytest.fixture +def check_user_prompt_closed_without_exception(session, create_dialog, checkbox_dom): + def check_user_prompt_closed_without_exception(dialog_type, retval): + session.url = checkbox_dom + element = session.find.css("custom-checkbox-element", all=False) + + create_dialog(dialog_type, text=dialog_type) + + response = get_shadow_root(session, element.id) + value = assert_success(response) + assert isinstance(value, dict) + assert "shadow-075b-4da1-b6ba-e579c2d3230a" in dict + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + return check_user_prompt_closed_without_exception + + +@pytest.fixture +def check_user_prompt_closed_with_exception(session, create_dialog, checkbox_dom): + def check_user_prompt_closed_with_exception(dialog_type, retval): + session.url = checkbox_dom + element = session.find.css("custom-checkbox-element", all=False) + + create_dialog(dialog_type, text=dialog_type) + + response = get_shadow_root(session, element.id) + assert_error(response, "unexpected alert open") + + assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) + + return check_user_prompt_closed_with_exception + + +@pytest.fixture +def check_user_prompt_not_closed_but_exception(session, create_dialog, checkbox_dom): + def check_user_prompt_not_closed_but_exception(dialog_type): + session.url = checkbox_dom + element = session.find.css("custom-checkbox-element", all=False) + + create_dialog(dialog_type, text=dialog_type) + + response = get_shadow_root(session, element.id) + assert_error(response, "unexpected alert open") + + assert session.alert.text == dialog_type + session.alert.dismiss() + + return check_user_prompt_not_closed_but_exception + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", True), + ("prompt", ""), +]) +def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): + check_user_prompt_closed_without_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): + check_user_prompt_not_closed_but_exception(dialog_type) + + +@pytest.mark.parametrize("dialog_type, retval", [ + ("alert", None), + ("confirm", False), + ("prompt", None), +]) +def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): + check_user_prompt_closed_with_exception(dialog_type, retval)