diff options
Diffstat (limited to 'testing/web-platform/tests/webdriver')
27 files changed, 765 insertions, 337 deletions
diff --git a/testing/web-platform/tests/webdriver/tests/bidi/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/__init__.py index 98b670f89f..c8715183b0 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/__init__.py @@ -71,6 +71,15 @@ def int_interval(start: int, end: int) -> Callable[[Any], None]: return _ +def assert_cookies(cookies, expected_cookies): + assert len(cookies) == len(expected_cookies) + + expected = sorted(expected_cookies, key=lambda cookie: cookie["name"]) + actual = sorted(cookies, key=lambda cookie: cookie["name"]) + + recursive_compare(expected, actual) + + def assert_handle(obj: Mapping[str, Any], should_contain_handle: bool) -> None: if should_contain_handle: assert "handle" in obj, f"Result should contain `handle`. Actual: {obj}" @@ -128,13 +137,21 @@ async def get_element_dimensions(bidi_session, context, element): return remote_mapping_to_dict(result["value"]) -async def get_viewport_dimensions(bidi_session, context: str): - expression = """ - ({ - height: window.innerHeight || document.documentElement.clientHeight, - width: window.innerWidth || document.documentElement.clientWidth, - }); - """ +async def get_viewport_dimensions(bidi_session, context: str, with_scrollbar: bool = True): + if with_scrollbar == True: + expression = """ + ({ + height: window.innerHeight, + width: window.innerWidth, + }); + """ + else: + expression = """ + ({ + height: document.documentElement.clientHeight, + width: document.documentElement.clientWidth, + }); + """ result = await bidi_session.script.evaluate( expression=expression, target=ContextTarget(context["context"]), diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py deleted file mode 100644 index 4f36fba197..0000000000 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest - -pytestmark = pytest.mark.asyncio - - -async def test_top_level_context_id_equals_window_handle(top_context, current_session): - assert top_context["context"] == current_session.window_handle diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py index 656eaddc1f..e560fa9239 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py @@ -48,134 +48,88 @@ async def test_find_by_locator(bidi_session, inline, top_context, type, value): recursive_compare(expected, result["nodes"]) -@pytest.mark.parametrize("ignore_case,match_type,max_depth,value,expected", [ - (True, "full", None, "bar", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "children": [], - "localName": "strong", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }, - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "full", None, "BAR", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "partial", None, "ba", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "strong", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }, - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "partial", None, "ba", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "full", 0, "foobarbarbaz", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "full", 0, "foobarBARbaz", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "partial", 0, "bar", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "partial", 0, "BAR", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ) +@pytest.mark.parametrize("locator,expected_nodes_values", [ + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "value": "bar" + }, ["strong", "span"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "value": "BAR" + }, ["span"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "value": "ba" + }, ["strong", "span"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "value": "ba" + }, ["strong"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "maxDepth": 0, + "value": "foobarbarbaz" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "maxDepth": 0, + "value": "foobarBARbaz" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "maxDepth": 0, + "value": "bar" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "maxDepth": 0, + "value": "BAR" + }, ["body"]), + ({ + + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "maxDepth": 1, + "value": "foobarbarbaz" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "maxDepth": 1, + "value": "foobarBARbaz" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "maxDepth": 1, + "value": "bar" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "maxDepth": 1, + "value": "BAR" + }, ["div"]), ], ids=[ "ignore_case_true_full_match_no_max_depth", "ignore_case_false_full_match_no_max_depth", @@ -185,23 +139,29 @@ async def test_find_by_locator(bidi_session, inline, top_context, type, value): "ignore_case_false_full_match_max_depth_zero", "ignore_case_true_partial_match_max_depth_zero", "ignore_case_false_partial_match_max_depth_zero", + "ignore_case_true_full_match_max_depth_one", + "ignore_case_false_full_match_max_depth_one", + "ignore_case_true_partial_match_max_depth_one", + "ignore_case_false_partial_match_max_depth_one", ]) @pytest.mark.asyncio -async def test_find_by_inner_text(bidi_session, inline, top_context, ignore_case, match_type, max_depth, value, expected): +async def test_find_by_inner_text(bidi_session, inline, top_context, locator, expected_nodes_values): url = inline("""<div>foo<span><strong>bar</strong></span><span>BAR</span>baz</div>""") await bidi_session.browsing_context.navigate( context=top_context["context"], url=url, wait="complete" ) + # Construct expected nodes list with the expected nodes values emitting other fields. + expected = [{ + "type": "node", + "value": { + "localName": node_value, + } + } for node_value in expected_nodes_values] + result = await bidi_session.browsing_context.locate_nodes( context=top_context["context"], - locator={ - "type": "innerText", - "value": value, - "ignoreCase": ignore_case, - "matchType": match_type, - "maxDepth": max_depth - } + locator=locator ) recursive_compare(expected, result["nodes"]) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py index 60f9e47040..2e8126b1f8 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py @@ -184,3 +184,65 @@ async def test_persists_on_reload(bidi_session, inline, new_tab): ) assert await get_viewport_dimensions(bidi_session, new_tab) == test_viewport + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "use_horizontal_scrollbar, use_vertical_scrollbar", + [ + (True, False), + (False, True), + (True, True), + ], + ids=["horizontal", "vertical", "both"], +) +@pytest.mark.parametrize( + "doctype", + ["html", "html_quirks"], + ids=["standard", "quirks"], +) +async def test_with_scrollbars( + bidi_session, + inline, + new_tab, + use_horizontal_scrollbar, + use_vertical_scrollbar, + doctype, +): + viewport_dimensions = await get_viewport_dimensions(bidi_session, new_tab) + + width = 100 + if use_horizontal_scrollbar: + width = viewport_dimensions["width"] + 100 + + height = 100 + if use_vertical_scrollbar: + height = viewport_dimensions["height"] + 100 + + html = f"""<div style="width: {width}px; height: {height}px;">foo</div>""" + page_url = inline(html, doctype=doctype) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_url, wait="complete" + ) + + test_viewport = {"width": 499, "height": 599} + + assert await get_viewport_dimensions(bidi_session, new_tab) != test_viewport + + await bidi_session.browsing_context.set_viewport( + context=new_tab["context"], viewport=test_viewport + ) + + assert await get_viewport_dimensions(bidi_session, new_tab) == test_viewport + + viewport_without_scrollbar = await get_viewport_dimensions( + bidi_session, new_tab, with_scrollbar=False + ) + + # The side which has scrollbar takes up space on the other side + # (e.g. if we have a horizontal scroll height is going to be smaller than viewport height) + if use_horizontal_scrollbar: + assert viewport_without_scrollbar["height"] < test_viewport["height"] + if use_vertical_scrollbar: + assert viewport_without_scrollbar["width"] < test_viewport["width"] diff --git a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py index 0ace04e8bc..5397dc7b62 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py @@ -52,3 +52,14 @@ async def test_params_origin_invalid_type(bidi_session, origin): state="granted", origin=origin, ) + + +@pytest.mark.parametrize("user_context", [False, 42, {}, [], None]) +async def test_params_origin_invalid_type(bidi_session, user_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.permissions.set_permission( + descriptor={"name": "geolocation"}, + state="granted", + origin="https://example.com", + user_context=user_context, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py index 92ebed1e63..45c50dbf88 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py @@ -16,7 +16,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin = await get_context_origin(bidi_session, new_tab) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -24,7 +24,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -32,7 +32,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "denied" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "denied" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -40,7 +40,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" @pytest.mark.asyncio @@ -73,7 +73,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): origin = await get_context_origin(bidi_session, new_tab) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -81,7 +81,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" new_context = await bidi_session.browsing_context.create(type_hint="tab") assert new_tab["context"] != new_context["context"] @@ -92,7 +92,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): ) # See https://github.com/w3c/permissions/issues/437. - assert await get_permission_state(bidi_session, new_context, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_context, "geolocation") == "granted" @pytest.mark.parametrize("origin", ['UNKNOWN', '']) @@ -117,4 +117,42 @@ async def test_set_permission_origin_unknown(bidi_session, new_tab, origin, url) state="granted", origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + + +@pytest.mark.asyncio +async def test_set_permission_user_context(bidi_session, new_tab, url, create_user_context): + test_url = url("/common/blank.html", protocol="https") + + user_context = await create_user_context() + # new_tab is in the default user context. new_tab2 is in the non-default user context. + new_tab2 = await bidi_session.browsing_context.create(type_hint="tab", user_context=user_context) + + # Navigate a context in the default user context. + await bidi_session.browsing_context.navigate( + context=new_tab["context"], + url=test_url, + wait="complete", + ) + + # Navigate a context in the non-default user context. + await bidi_session.browsing_context.navigate( + context=new_tab2["context"], + url=test_url, + wait="complete", + ) + + origin = await get_context_origin(bidi_session, new_tab) + + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab2, "geolocation") == "prompt" + + await bidi_session.permissions.set_permission( + descriptor={"name": "geolocation"}, + state="granted", + origin=origin, + user_context=user_context, + ) + + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab2, "geolocation") == "granted" diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/integration/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/integration/__init__.py diff --git a/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py b/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py new file mode 100644 index 0000000000..e7fddbb1c4 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py @@ -0,0 +1,201 @@ +import pytest + +from webdriver.bidi.modules.script import ContextTarget +from webdriver.bidi.modules.storage import BrowsingContextPartitionDescriptor + +from .. import assert_cookies + +pytestmark = pytest.mark.asyncio + +PNG_BLACK_DOT = "/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png" + + +async def test_top_context( + bidi_session, + new_tab, + inline, + setup_network_test, + wait_for_event, + wait_for_future_safe, +): + cookie_name = "foo" + cookie_value = "bar" + url = inline( + "<div>with cookies</div>", + parameters={"pipe": f"header(Set-Cookie, {cookie_name}={cookie_value})"}, + ) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=url, wait="complete" + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + + await bidi_session.browsing_context.reload( + context=new_tab["context"], wait="complete" + ) + + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + + assert_cookies(result["cookies"], events[0]["request"]["cookies"]) + + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_iframe( + bidi_session, + new_tab, + inline, + setup_network_test, + wait_for_event, + wait_for_future_safe, + domain_1, +): + cookie_name = "bar" + cookie_value = "foo" + iframe_url = inline( + "<div id='in-iframe'>with cookies</div>", + domain=domain_1, + parameters={"pipe": f"header(Set-Cookie, {cookie_name}={cookie_value})"}, + ) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=iframe_url, wait="complete" + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + + page_url = inline(f"<iframe src='{iframe_url}'></iframe>") + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_url, wait="complete" + ) + + await wait_for_future_safe(on_before_request_sent) + + all_contexts = await bidi_session.browsing_context.get_tree(root=new_tab["context"]) + iframe_context = all_contexts[0]["children"][0]["context"] + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(iframe_context) + ) + + # Find the network event which belongs to the iframe. + event_for_iframe = next( + event for event in events if event["context"] == iframe_context + ) + + assert_cookies(result["cookies"], event_for_iframe["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_fetch( + bidi_session, + new_tab, + setup_network_test, + wait_for_event, + fetch, + wait_for_future_safe, + url, + domain_1, +): + # Clean up cookies in case some other tests failed before cleaning up. + await bidi_session.storage.delete_cookies() + + cookie_name = "foo" + cookie_value = "bar" + # Add `Access-Control-Allow-Origin` header for cross-origin request to work. + request_url = url( + "/webdriver/tests/support/http_handlers/headers.py?header=Access-Control-Allow-Origin:*", + domain=domain_1, + ) + + await bidi_session.script.evaluate( + expression=f"document.cookie = '{cookie_name}={cookie_value}';", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await fetch(request_url, method="GET") + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + assert_cookies(result["cookies"], events[0]["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_image( + bidi_session, + new_tab, + setup_network_test, + wait_for_event, + wait_for_future_safe, + url, + inline, + domain_1, +): + # Clean up cookies in case some other tests failed before cleaning up. + await bidi_session.storage.delete_cookies() + + cookie_name = "bar" + cookie_value = "foo" + + image_url = url(PNG_BLACK_DOT) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=image_url, wait="complete" + ) + + await bidi_session.script.evaluate( + expression=f"document.cookie = '{cookie_name}={cookie_value}';", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + page_with_image = inline(f"<img src='{image_url}'>", domain=domain_1) + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_with_image, wait="complete" + ) + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + + # Find the network event which belongs to the image. + event_for_image = next( + event for event in events if event["request"]["url"] == image_url + ) + assert_cookies(result["cookies"], event_for_image["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py index 9bbc6f5daf..2e6376287b 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py @@ -6,6 +6,7 @@ from .. import ( any_list, any_string, any_string_or_null, + assert_cookies, recursive_compare, ) @@ -15,21 +16,6 @@ def assert_bytes_value(bytes_value): any_string(bytes_value["value"]) -def assert_cookies(event_cookies, expected_cookies): - assert len(event_cookies) == len(expected_cookies) - - # Simple helper to find a cookie by key and value only. - def match_cookie(cookie, expected): - for key in expected: - if cookie[key] != expected[key]: - return False - - return True - - for cookie in expected_cookies: - assert next(c for c in event_cookies if match_cookie(c, cookie)) is not None - - def assert_headers(event_headers, expected_headers): # The browser sets request headers, only assert that the expected headers # are included in the request's headers. @@ -349,3 +335,9 @@ BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" FETCH_ERROR_EVENT = "network.fetchError" RESPONSE_COMPLETED_EVENT = "network.responseCompleted" RESPONSE_STARTED_EVENT = "network.responseStarted" + +PHASE_TO_EVENT_MAP = { + "authRequired": [AUTH_REQUIRED_EVENT, assert_response_event], + "beforeRequestSent": [BEFORE_REQUEST_SENT_EVENT, assert_before_request_sent_event], + "responseStarted": [RESPONSE_STARTED_EVENT, assert_response_event], +} diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py b/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py index 83dfa5560f..7606b2368b 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py @@ -5,15 +5,69 @@ from webdriver.bidi.modules.script import ScriptEvaluateResultException from .. import ( assert_before_request_sent_event, + assert_response_event, PAGE_EMPTY_HTML, PAGE_EMPTY_TEXT, BEFORE_REQUEST_SENT_EVENT, RESPONSE_COMPLETED_EVENT, RESPONSE_STARTED_EVENT, + PHASE_TO_EVENT_MAP, ) @pytest.mark.asyncio +@pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) +@pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) +async def test_frame_context( + bidi_session, + url, + inline, + top_context, + add_intercept, + fetch, + setup_network_test, + wait_for_event, + wait_for_future_safe, + domain, + phase +): + await setup_network_test( + events=[ + BEFORE_REQUEST_SENT_EVENT, + RESPONSE_STARTED_EVENT, + RESPONSE_COMPLETED_EVENT, + ], + contexts=[top_context["context"]], + ) + + frame_url = inline("<div>foo</div>") + test_url = inline(f"<iframe src='{frame_url}'></iframe>", domain=domain) + await bidi_session.browsing_context.navigate( + url=test_url, context=top_context["context"], wait="complete" + ) + + # Retrieve the context for the iframe. + contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"]) + assert len(contexts[0]["children"]) == 1 + frame = contexts[0]["children"][0] + + # Add an intercept. + text_url = url(PAGE_EMPTY_TEXT) + await add_intercept( + phases=[phase], + url_patterns=[{"type": "string", "pattern": text_url}], + contexts=[top_context["context"]], + ) + + # Request in the iframe context should be blocked. + [event_name, assert_network_event] = PHASE_TO_EVENT_MAP[phase] + on_network_event = wait_for_event(event_name) + asyncio.ensure_future(fetch(text_url, context=frame)) + event = await wait_for_future_safe(on_network_event) + assert_network_event(event, is_blocked=True) + + +@pytest.mark.asyncio @pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) async def test_other_context( bidi_session, @@ -22,7 +76,9 @@ async def test_other_context( add_intercept, fetch, setup_network_test, - phase, + wait_for_event, + wait_for_future_safe, + phase ): # Subscribe to network events only in top_context await setup_network_test( @@ -47,13 +103,17 @@ async def test_other_context( url_patterns=[{"type": "string", "pattern": text_url}], ) - # Request to top_context should be blocked and throw a ScriptEvaluateResultException - # from the AbortController. - with pytest.raises(ScriptEvaluateResultException): - await fetch(text_url, context=top_context) - # Request to other_context should not be blocked. - await fetch(text_url, context=other_context) + # Request to top_context should be blocked. + [event_name, assert_network_event] = PHASE_TO_EVENT_MAP[phase] + on_network_event = wait_for_event(event_name) + asyncio.ensure_future(fetch(text_url, context=top_context)) + event = await wait_for_future_safe(on_network_event) + assert_network_event(event, is_blocked=True) + + # Request to other_context should not be blocked because we are not + # subscribed to network events. Wait for fetch to resolve successfully. + await asyncio.ensure_future(fetch(text_url, context=other_context)) @pytest.mark.asyncio diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py b/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py index c92337e507..95a790e37c 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py @@ -393,3 +393,26 @@ async def test_redirect_navigation( # Check that both requests share the same requestId assert events[0]["request"]["request"] == events[1]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_before_request_sent) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL in requestData. + assert_before_request_sent_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py b/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py index 7813530c4c..424fa8b5c7 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py @@ -1,13 +1,9 @@ -import json - import asyncio -import pytest import pytest_asyncio from webdriver.bidi.error import NoSuchInterceptException -from webdriver.bidi.modules.script import ContextTarget -from . import PAGE_EMPTY_HTML, PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT +from . import PAGE_EMPTY_TEXT @pytest_asyncio.fixture @@ -40,102 +36,6 @@ async def add_intercept(bidi_session): pass -@pytest.fixture -def fetch(bidi_session, top_context, configuration): - """Perform a fetch from the page of the provided context, default to the - top context. - """ - - async def fetch( - url, method="GET", headers=None, context=top_context, timeout_in_seconds=3 - ): - method_arg = f"method: '{method}'," - - headers_arg = "" - if headers is not None: - headers_arg = f"headers: {json.dumps(headers)}," - - timeout_in_seconds = timeout_in_seconds * configuration["timeout_multiplier"] - - # Wait for fetch() to resolve a response and for response.text() to - # resolve as well to make sure the request/response is completed when - # the helper returns. - await bidi_session.script.evaluate( - expression=f""" - {{ - const controller = new AbortController(); - setTimeout(() => controller.abort(), {timeout_in_seconds * 1000}); - fetch("{url}", {{ - {method_arg} - {headers_arg} - signal: controller.signal - }}).then(response => response.text()); - }}""", - target=ContextTarget(context["context"]), - await_promise=True, - ) - - return fetch - - -@pytest_asyncio.fixture -async def setup_network_test( - bidi_session, - subscribe_events, - wait_for_event, - wait_for_future_safe, - top_context, - url, -): - """Navigate the current top level context to the provided url and subscribe - to network.beforeRequestSent. - - Returns an `events` dictionary in which the captured network events will be added. - The keys of the dictionary are network event names (eg. "network.beforeRequestSent"), - and the value is an array of collected events. - """ - listeners = [] - - async def _setup_network_test(events, test_url=url(PAGE_EMPTY_HTML), contexts=None): - nonlocal listeners - - # Listen for network.responseCompleted for the initial navigation to - # make sure this event will not be captured unexpectedly by the tests. - await bidi_session.session.subscribe( - events=[RESPONSE_COMPLETED_EVENT], contexts=[top_context["context"]] - ) - on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT) - - await bidi_session.browsing_context.navigate( - context=top_context["context"], - url=test_url, - wait="complete", - ) - await wait_for_future_safe(on_response_completed) - await bidi_session.session.unsubscribe( - events=[RESPONSE_COMPLETED_EVENT], contexts=[top_context["context"]] - ) - - await subscribe_events(events, contexts) - - network_events = {} - for event in events: - network_events[event] = [] - - async def on_event(method, data, event=event): - network_events[event].append(data) - - listeners.append(bidi_session.add_event_listener(event, on_event)) - - return network_events - - yield _setup_network_test - - # cleanup - for remove_listener in listeners: - remove_listener() - - @pytest_asyncio.fixture async def setup_blocked_request( bidi_session, diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py b/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py index de9492f0a5..f8cc3fb676 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py @@ -11,7 +11,7 @@ pytestmark = pytest.mark.asyncio @pytest.mark.parametrize("navigate", [False, True], ids=["fetch", "navigate"]) async def test_provide_response_auth_required( - setup_blocked_request, subscribe_events, wait_for_event, bidi_session, navigate + setup_blocked_request, subscribe_events, wait_for_event, bidi_session, navigate, wait_for_future_safe ): request = await setup_blocked_request("authRequired", navigate=navigate) @@ -28,13 +28,13 @@ async def test_provide_response_auth_required( await bidi_session.network.provide_response(request=request) - await on_auth_required + await wait_for_future_safe(on_auth_required) @pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) @pytest.mark.parametrize("navigate", [False, True], ids=["fetch", "navigate"]) async def test_provide_response_phase( - setup_blocked_request, subscribe_events, wait_for_event, bidi_session, phase, navigate + setup_blocked_request, subscribe_events, wait_for_event, bidi_session, phase, navigate, wait_for_future_safe ): request = await setup_blocked_request(phase, navigate=navigate) @@ -58,10 +58,10 @@ async def test_provide_response_phase( await bidi_session.network.provide_response(request=request) - await on_response_completed + await wait_for_future_safe(on_response_completed) if phase == "beforeRequestSent": - await on_response_started + await wait_for_future_safe(on_response_started) if navigate: - await on_load + await wait_for_future_safe(on_load) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py b/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py index b9b4ae727e..56b9461642 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py @@ -368,3 +368,28 @@ async def test_redirect_document( # Check that the last 2 requests share the same request id assert events[1]["request"]["request"] == events[2]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[RESPONSE_COMPLETED_EVENT]) + events = network_events[RESPONSE_COMPLETED_EVENT] + + on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_response_completed) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL both in requestData + # and responseData + assert_response_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + expected_response={"url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py b/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py index fb99073fb3..6c10714ca8 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py @@ -311,3 +311,28 @@ async def test_redirect(bidi_session, url, fetch, setup_network_test): # Check that both requests share the same requestId assert events[0]["request"]["request"] == events[1]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[RESPONSE_STARTED_EVENT]) + events = network_events[RESPONSE_STARTED_EVENT] + + on_response_started = wait_for_event(RESPONSE_STARTED_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_response_started) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL both in requestData + # and responseData + assert_response_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + expected_response={"url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py index 7feae91f27..4971737f8c 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py @@ -155,16 +155,16 @@ REMOTE_VALUES: list[tuple[str, dict]] = [ ("new WeakSet()", {"type": "weakset", },), ("new Error('SOME_ERROR_TEXT')", {"type": "error"},), ("[1, 2][Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("'mystring'[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("(new Set([1,2]))[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("(new Map([[1,2]]))[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("new Proxy({}, {})", { "type": "proxy", diff --git a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py index 632e7ffa26..dbe882b8cf 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py @@ -159,7 +159,7 @@ async def test_partition_context_with_different_domain( partition=BrowsingContextPartitionDescriptor(new_tab["context"]) ) - assert result["cookies"] == [ + recursive_compare([ { "domain": cookie_domain, "httpOnly": False, @@ -170,7 +170,7 @@ async def test_partition_context_with_different_domain( "size": 6, "value": {"type": "string", "value": cookie_value}, } - ] + ], result["cookies"]) @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png Binary files differnew file mode 100644 index 0000000000..613754cfaf --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png diff --git a/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py b/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py index d55860c874..65f8a9015e 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py +++ b/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py @@ -42,8 +42,22 @@ def test_disabled(session, inline): assert_success(response) +@pytest.mark.parametrize("transform", ["translate(100px, 100px)", "rotate(50deg)"]) +def test_element_interactable_css_transform(session, inline, transform): + # The button is transformed within the viewport. + session.url = inline(""" + <div style="width: 500px; height: 100px; position: absolute; left: 50px; top: 200px; + background-color: blue; transform: {transform};"> + <input type=button> + </div>""".format(transform=transform)) + element = session.find.css("input", all=False) + response = element_click(session, element) + assert_success(response) + + @pytest.mark.parametrize("transform", ["translate(-100px, -100px)", "rotate(50deg)"]) def test_element_not_interactable_css_transform(session, inline, transform): + # The button is transformed outside of the viewport. session.url = inline(""" <div style="width: 500px; height: 100px; background-color: blue; transform: {transform};"> diff --git a/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py b/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py index 591847e881..041f0dee6a 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py +++ b/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py @@ -31,9 +31,9 @@ def test_scroll_into_view(session, inline): assert session.execute_script(""" let input = arguments[0]; rect = input.getBoundingClientRect(); - return rect["top"] >= 0 && rect["left"] >= 0 && - (rect["top"] + rect["height"]) <= window.innerHeight && - (rect["left"] + rect["width"]) <= window.innerWidth; + return rect.top >= 0 && rect.left >= 0 && + Math.floor(rect.bottom) <= window.innerHeight && + Math.floor(rect.right) <= window.innerWidth; """, args=(element,)) is True @@ -69,4 +69,4 @@ def test_partially_visible_does_not_scroll(session, offset, inline): assert_success(response) assert session.execute_script("return window.scrollY || document.documentElement.scrollTop") == 0 click_point = assert_one_click(session) - assert click_point == center_point(target) + assert click_point == pytest.approx(center_point(target), abs=1.0) diff --git a/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py b/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py index f777d6a767..184dc4234e 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py +++ b/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py @@ -1,9 +1,8 @@ import pytest -from webdriver import NoSuchElementException, NoSuchWindowException +from webdriver import NoSuchElementException from tests.support.asserts import assert_error, assert_success -from tests.support.sync import Poll def switch_to_parent_frame(session): @@ -35,37 +34,6 @@ def test_no_top_browsing_context(session, url): assert_error(response, "no such window") -def test_no_parent_browsing_context(session, url): - session.url = url("/webdriver/tests/support/html/frames.html") - - subframe = session.find.css("#sub-frame", all=False) - session.switch_frame(subframe) - - deleteframe = session.find.css("#delete-frame", all=False) - session.switch_frame(deleteframe) - - button = session.find.css("#remove-top", all=False) - button.click() - - def is_window_closed(s): - try: - s.find.css("#remove-top", all=False) - return False - except NoSuchWindowException: - return True - - # Wait until iframe is gone. - wait = Poll( - session, - timeout=5, - message="Iframe is still present", - ) - wait.until(lambda s: is_window_closed(s)) - - response = switch_to_parent_frame(session) - assert_error(response, "no such window") - - def test_no_browsing_context(session, closed_frame): response = switch_to_parent_frame(session) assert_success(response) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/__init__.py b/testing/web-platform/tests/webdriver/tests/interop/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/interop/__init__.py diff --git a/testing/web-platform/tests/webdriver/tests/interop/frames.py b/testing/web-platform/tests/webdriver/tests/interop/frames.py new file mode 100644 index 0000000000..b2cafb4987 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/interop/frames.py @@ -0,0 +1,37 @@ +import pytest +from webdriver.error import NoSuchWindowException + +from tests.support.sync import AsyncPoll + +pytestmark = pytest.mark.asyncio + + +async def test_classic_switch_to_parent_no_browsing_context(bidi_session, current_session, url): + # With WebDriver classic it cannot be checked if the parent frame is already + # gone before switching to it. To prevent race conditions such a check needs + # to be done via WebDriver BiDi. + current_session.url = url("/webdriver/tests/support/html/frames.html") + + subframe = current_session.find.css("#sub-frame", all=False) + current_session.switch_frame(subframe) + + deleteframe = current_session.find.css("#delete-frame", all=False) + current_session.switch_frame(deleteframe) + + button = current_session.find.css("#remove-top", all=False) + button.click() + + async def is_frame_removed(_): + contexts = await bidi_session.browsing_context.get_tree(root=current_session.window_handle) + return not contexts[0]["children"] + + # Wait until IFrame is gone. + wait = AsyncPoll( + current_session, + timeout=5, + message="IFrame that should be closed is still open", + ) + await wait.until(is_frame_removed) + + with pytest.raises(NoSuchWindowException): + current_session.switch_frame("parent") diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/node_shared_id.py b/testing/web-platform/tests/webdriver/tests/interop/shared_id_node.py index aeb2bc4597..aeb2bc4597 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/node_shared_id.py +++ b/testing/web-platform/tests/webdriver/tests/interop/shared_id_node.py diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/window_reference.py b/testing/web-platform/tests/webdriver/tests/interop/shared_id_window.py index 1588303be0..d13262b4e5 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/window_reference.py +++ b/testing/web-platform/tests/webdriver/tests/interop/shared_id_window.py @@ -6,6 +6,10 @@ from webdriver.bidi.modules.script import ContextTarget pytestmark = pytest.mark.asyncio +async def test_top_level_context_id_equals_window_handle(top_context, current_session): + assert top_context["context"] == current_session.window_handle + + async def test_web_window_reference_created_in_classic( bidi_session, current_session, diff --git a/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py b/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py index 7f3e4f9a9a..32919210bf 100644 --- a/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py +++ b/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py @@ -1,15 +1,16 @@ -import base64 - -from tests.support.asserts import assert_pdf -from tests.support.image import cm_to_px, png_dimensions, ImageDifference -from typing import Any, Coroutine, Mapping - import asyncio +import base64 import copy +import json +import time from datetime import datetime, timedelta +from typing import Any, Coroutine, Mapping + import pytest import pytest_asyncio -import time + +from tests.support.asserts import assert_pdf +from tests.support.image import cm_to_px, png_dimensions, ImageDifference from webdriver.bidi.error import ( InvalidArgumentException, NoSuchFrameException, @@ -528,3 +529,98 @@ def domain_value(server_config): return server_config["domains"][domain][subdomain] return domain_value + + +@pytest.fixture +def fetch(bidi_session, top_context, configuration): + """Perform a fetch from the page of the provided context, default to the + top context. + """ + + async def fetch( + url, method="GET", headers=None, context=top_context, timeout_in_seconds=3 + ): + method_arg = f"method: '{method}'," + + headers_arg = "" + if headers is not None: + headers_arg = f"headers: {json.dumps(headers)}," + + timeout_in_seconds = timeout_in_seconds * configuration["timeout_multiplier"] + # Wait for fetch() to resolve a response and for response.text() to + # resolve as well to make sure the request/response is completed when + # the helper returns. + await bidi_session.script.evaluate( + expression=f""" + {{ + const controller = new AbortController(); + setTimeout(() => controller.abort(), {timeout_in_seconds * 1000}); + fetch("{url}", {{ + {method_arg} + {headers_arg} + signal: controller.signal, + }}).then(response => response.text()); + }}""", + target=ContextTarget(context["context"]), + await_promise=True, + ) + + return fetch + + +@pytest_asyncio.fixture +async def setup_network_test( + bidi_session, + subscribe_events, + wait_for_event, + wait_for_future_safe, + top_context, + url, +): + """Navigate the current top level context to the provided url and subscribe + to network.beforeRequestSent. + + Returns an `events` dictionary in which the captured network events will be added. + The keys of the dictionary are network event names (eg. "network.beforeRequestSent"), + and the value is an array of collected events. + """ + listeners = [] + + async def _setup_network_test(events, test_url=url("/webdriver/tests/bidi/network/support/empty.html"), contexts=None): + nonlocal listeners + + # Listen for network.responseCompleted for the initial navigation to + # make sure this event will not be captured unexpectedly by the tests. + await bidi_session.session.subscribe( + events=["network.responseCompleted"], contexts=[top_context["context"]] + ) + on_response_completed = wait_for_event("network.responseCompleted") + + await bidi_session.browsing_context.navigate( + context=top_context["context"], + url=test_url, + wait="complete", + ) + await wait_for_future_safe(on_response_completed) + await bidi_session.session.unsubscribe( + events=["network.responseCompleted"], contexts=[top_context["context"]] + ) + + await subscribe_events(events, contexts) + + network_events = {} + for event in events: + network_events[event] = [] + + async def on_event(method, data, event=event): + network_events[event].append(data) + + listeners.append(bidi_session.add_event_listener(event, on_event)) + + return network_events + + yield _setup_network_test + + # cleanup + for remove_listener in listeners: + remove_listener() diff --git a/testing/web-platform/tests/webdriver/tests/support/inline.py b/testing/web-platform/tests/webdriver/tests/support/inline.py index ecb2a2587b..8364e0590e 100644 --- a/testing/web-platform/tests/webdriver/tests/support/inline.py +++ b/testing/web-platform/tests/webdriver/tests/support/inline.py @@ -6,6 +6,7 @@ from urllib.parse import urlencode BOILERPLATES = { "html": "<!doctype html>\n<meta charset={charset}>\n{src}", + "html_quirks": "{src}", "xhtml": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> @@ -22,6 +23,7 @@ BOILERPLATES = { } MIME_TYPES = { "html": "text/html", + "html_quirks": "text/html", "xhtml": "application/xhtml+xml", "xml": "text/xml", "js": "text/javascript", |