diff --git a/tests/sync/test_assertions.py b/tests/sync/test_assertions.py
index 90cf5a3be..99af93286 100644
--- a/tests/sync/test_assertions.py
+++ b/tests/sync/test_assertions.py
@@ -14,6 +14,7 @@
import datetime
import re
+from typing import Any, Callable, Generator
import pytest
@@ -355,7 +356,16 @@ def test_ignore_case_regex(page: Page, server: Server, method: str) -> None:
)
assert expected_error_msg in str(excinfo.value)
- # Array Variants
+
+@pytest.mark.parametrize(
+ "method",
+ ["to_have_text", "to_contain_text"],
+)
+def test_ignore_case_regex_array_variants(
+ page: Page, server: Server, method: str
+) -> None:
+ page.goto(server.EMPTY_PAGE)
+ page.set_content("
apple BANANA
orange
")
getattr(expect(page.locator("div")), method)(
[re.compile("apple BANANA"), re.compile("orange")]
)
@@ -368,6 +378,7 @@ def test_ignore_case_regex(page: Page, server: Server, method: str) -> None:
[re.compile("apple banana"), re.compile("ORANGE")],
timeout=300,
)
+ expected_error_msg = method.replace("_", " ")
assert expected_error_msg in str(excinfo.value)
# overrides regex flag
with pytest.raises(AssertionError) as excinfo:
@@ -381,7 +392,15 @@ def test_ignore_case_regex(page: Page, server: Server, method: str) -> None:
)
assert expected_error_msg in str(excinfo.value)
- # not variant
+
+@pytest.mark.parametrize(
+ "method",
+ ["to_have_text", "to_contain_text"],
+)
+def test_ignore_case_regex_not_variant(page: Page, server: Server, method: str) -> None:
+ page.goto(server.EMPTY_PAGE)
+ page.set_content("apple BANANA
orange
")
+ expected_error_msg = method.replace("_", " ")
getattr(expect(page.locator("div#target")), f"not_{method}")(
re.compile("apple banana")
)
@@ -789,62 +808,61 @@ def test_assertions_response_is_ok_fail(page: Page, server: Server) -> None:
assert "← 404 Not Found" in error_message
-def test_should_print_response_with_text_content_type_if_to_be_ok_fails(
- page: Page, server: Server
-) -> None:
- server.set_route(
- "/text-content-type",
- lambda r: (
- r.setResponseCode(404),
- r.setHeader("content-type", "text/plain"),
- r.write(b"Text error"),
- r.finish(),
+@pytest.mark.parametrize(
+ "path, handler, body, shows_text",
+ [
+ (
+ "/text-content-type",
+ lambda r: (
+ r.setResponseCode(404),
+ r.setHeader("content-type", "text/plain"),
+ r.write(b"Text error"),
+ r.finish(),
+ ),
+ "Text error",
+ True,
),
- )
- server.set_route(
- "/no-content-type",
- lambda r: (
- r.setResponseCode(404),
- r.write(b"No content type error"),
- r.finish(),
+ (
+ "/no-content-type",
+ lambda r: (
+ r.setResponseCode(404),
+ r.write(b"No content type error"),
+ r.finish(),
+ ),
+ "No content type error",
+ False,
),
- )
- server.set_route(
- "/binary-content-type",
- lambda r: (
- r.setResponseCode(404),
- r.setHeader("content-type", "image/bmp"),
- r.write(b"Image content type error"),
- r.finish(),
+ (
+ "/binary-content-type",
+ lambda r: (
+ r.setResponseCode(404),
+ r.setHeader("content-type", "image/bmp"),
+ r.write(b"Image content type error"),
+ r.finish(),
+ ),
+ "Image content type error",
+ False,
),
- )
-
- response = page.request.get(server.PREFIX + "/text-content-type")
- with pytest.raises(AssertionError) as excinfo:
- expect(response).to_be_ok()
- error_message = str(excinfo.value)
- assert ("→ GET " + server.PREFIX + "/text-content-type") in error_message
- assert "← 404 Not Found" in error_message
- assert "Response Text:" in error_message
- assert "Text error" in error_message
-
- response = page.request.get(server.PREFIX + "/no-content-type")
- with pytest.raises(AssertionError) as excinfo:
- expect(response).to_be_ok()
- error_message = str(excinfo.value)
- assert ("→ GET " + server.PREFIX + "/no-content-type") in error_message
- assert "← 404 Not Found" in error_message
- assert "Response Text:" not in error_message
- assert "No content type error" not in error_message
+ ],
+)
+def test_should_print_response_with_text_content_type_if_to_be_ok_fails(
+ page: Page,
+ server: Server,
+ path: str,
+ handler: Callable[[Any], Any],
+ body: str,
+ shows_text: bool,
+) -> None:
+ server.set_route(path, handler)
- response = page.request.get(server.PREFIX + "/binary-content-type")
+ response = page.request.get(server.PREFIX + path)
with pytest.raises(AssertionError) as excinfo:
expect(response).to_be_ok()
error_message = str(excinfo.value)
- assert ("→ GET " + server.PREFIX + "/binary-content-type") in error_message
+ assert ("→ GET " + server.PREFIX + path) in error_message
assert "← 404 Not Found" in error_message
- assert "Response Text:" not in error_message
- assert "Image content type error" not in error_message
+ assert ("Response Text:" in error_message) == shows_text
+ assert (body in error_message) == shows_text
def test_should_print_users_message_for_page_based_assertion(
@@ -972,16 +990,21 @@ def test_should_be_able_to_set_custom_timeout(page: Page) -> None:
)
-def test_should_be_able_to_set_custom_global_timeout(page: Page) -> None:
- try:
- expect.set_options(timeout=111)
- with pytest.raises(AssertionError) as exc_info:
- expect(page.locator("#a1")).to_be_visible()
- assert 'Expect "to_be_visible" locator("#a1") with timeout 111ms' in str(
- exc_info.value
- )
- finally:
- expect.set_options(timeout=5_000)
+@pytest.fixture
+def custom_global_timeout() -> Generator[None, None, None]:
+ expect.set_options(timeout=111)
+ yield
+ expect.set_options(timeout=5_000)
+
+
+def test_should_be_able_to_set_custom_global_timeout(
+ page: Page, custom_global_timeout: None
+) -> None:
+ with pytest.raises(AssertionError) as exc_info:
+ expect(page.locator("#a1")).to_be_visible()
+ assert 'Expect "to_be_visible" locator("#a1") with timeout 111ms' in str(
+ exc_info.value
+ )
def test_to_have_accessible_name(page: Page) -> None:
diff --git a/tests/sync/test_browsercontext_request_fallback.py b/tests/sync/test_browsercontext_request_fallback.py
index 6feb19942..a13e26822 100644
--- a/tests/sync/test_browsercontext_request_fallback.py
+++ b/tests/sync/test_browsercontext_request_fallback.py
@@ -117,8 +117,7 @@ def test_should_not_chain_abort(
page: Page,
context: BrowserContext,
server: Server,
- is_webkit: bool,
- is_firefox: bool,
+ browser_name: str,
) -> None:
failed = [False]
@@ -131,12 +130,11 @@ def handler(route: Route) -> None:
with pytest.raises(Error) as excinfo:
page.goto(server.EMPTY_PAGE)
- if is_webkit:
- assert "Blocked by Web Inspector" in excinfo.value.message
- elif is_firefox:
- assert "NS_ERROR_FAILURE" in excinfo.value.message
- else:
- assert "net::ERR_FAILED" in excinfo.value.message
+ expected_message = {
+ "webkit": "Blocked by Web Inspector",
+ "firefox": "NS_ERROR_FAILURE",
+ }.get(browser_name, "net::ERR_FAILED")
+ assert expected_message in excinfo.value.message
assert not failed[0]
@@ -180,6 +178,25 @@ def handler_with_header_mods(route: Route) -> None:
assert values == ["bar", "bar", "bar"]
+FETCH_WITH_FOO_AND_BAR_HEADERS_JS = """
+ async url => {
+ const data = await fetch(url, {
+ headers: {
+ foo: 'a',
+ bar: 'b',
+ }
+ });
+ return data.text();
+ }
+ """
+
+
+def delete_foo_header(route: Route, request: Request) -> None:
+ headers = request.all_headers()
+ del headers["foo"]
+ route.fallback(headers=headers)
+
+
def test_should_delete_header_with_undefined_value(
page: Page, context: BrowserContext, server: Server
) -> None:
@@ -200,27 +217,10 @@ def capture_and_continue(route: Route, request: Request) -> None:
route.continue_()
context.route("**/*", capture_and_continue)
-
- def delete_foo_header(route: Route, request: Request) -> None:
- headers = request.all_headers()
- del headers["foo"]
- route.fallback(headers=headers)
-
context.route(server.PREFIX + "/something", delete_foo_header)
with server.expect_request("/something") as server_req_info:
text = page.evaluate(
- """
- async url => {
- const data = await fetch(url, {
- headers: {
- foo: 'a',
- bar: 'b',
- }
- });
- return data.text();
- }
- """,
- server.PREFIX + "/something",
+ FETCH_WITH_FOO_AND_BAR_HEADERS_JS, server.PREFIX + "/something"
)
server_req = server_req_info.value
assert text == "done"
diff --git a/tests/sync/test_browsercontext_storage_state.py b/tests/sync/test_browsercontext_storage_state.py
index db2a06748..a0c9a9497 100644
--- a/tests/sync/test_browsercontext_storage_state.py
+++ b/tests/sync/test_browsercontext_storage_state.py
@@ -14,6 +14,7 @@
import json
from pathlib import Path
+from typing import Generator
import pytest
@@ -43,7 +44,8 @@ def test_should_capture_local_storage(context: BrowserContext) -> None:
}
-def test_should_set_local_storage(browser: Browser) -> None:
+@pytest.fixture
+def page_with_storage_state(browser: Browser) -> Generator[Page, None, None]:
storage_state: StorageState = {
"origins": [
{
@@ -72,10 +74,17 @@ def test_should_set_local_storage(browser: Browser) -> None:
page = context.new_page()
page.route("**/*", lambda route: route.fulfill(body=""))
page.goto("https://www.example.com")
- local_storage = page.evaluate("window.localStorage")
+ yield page
+ context.close()
+
+
+def test_should_set_local_storage(page_with_storage_state: Page) -> None:
+ local_storage = page_with_storage_state.evaluate("window.localStorage")
assert local_storage == {"name1": "value1"}
- indexed_db = page.evaluate(
+
+def test_should_set_indexed_db(page_with_storage_state: Page) -> None:
+ indexed_db = page_with_storage_state.evaluate(
"""async () => {
return new Promise((resolve, reject) => {
const openRequest = indexedDB.open('db', 42);
@@ -91,7 +100,6 @@ def test_should_set_local_storage(browser: Browser) -> None:
}"""
)
assert indexed_db == "foo"
- context.close()
def test_should_round_trip_through_the_file(
diff --git a/tests/sync/test_browsertype_connect.py b/tests/sync/test_browsertype_connect.py
index 74970c545..6157e24b0 100644
--- a/tests/sync/test_browsertype_connect.py
+++ b/tests/sync/test_browsertype_connect.py
@@ -247,5 +247,4 @@ def test_set_input_files_should_preserve_last_modified_timestamp(
# On Linux browser sometimes reduces the timestamp by 1ms: 1696272058110.0715 -> 1696272058109 or even
# rounds it to seconds in WebKit: 1696272058110 -> 1696272058000.
- for i in range(len(timestamps)):
- assert abs(timestamps[i] - expected_timestamps[i]) < 1000
+ assert timestamps == pytest.approx(expected_timestamps, abs=1000)
diff --git a/tests/sync/test_console.py b/tests/sync/test_console.py
index 60680a46b..241c95311 100644
--- a/tests/sync/test_console.py
+++ b/tests/sync/test_console.py
@@ -32,12 +32,11 @@ def test_console_should_work(page: Page, browser_name: str) -> None:
page.evaluate('() => console.log("hello", 5, {foo: "bar"})')
assert len(messages) == 1
message = messages[0]
- if browser_name != "firefox":
- assert message.text == "hello 5 {foo: bar}"
- assert str(message) == "hello 5 {foo: bar}"
- else:
- assert message.text == "hello 5 JSHandle@object"
- assert str(message) == "hello 5 JSHandle@object"
+ expected_text = {"firefox": "hello 5 JSHandle@object"}.get(
+ browser_name, "hello 5 {foo: bar}"
+ )
+ assert message.text == expected_text
+ assert str(message) == expected_text
assert message.type == "log"
assert message.args[0].json_value() == "hello"
assert message.args[1].json_value() == 5
@@ -102,10 +101,8 @@ def test_console_should_not_fail_for_window_object(
with page.expect_console_message():
page.evaluate("() => console.error(window)")
assert len(messages) == 1
- if browser_name != "firefox":
- assert messages[0].text == "Window"
- else:
- assert messages[0].text == "JSHandle@object"
+ expected_text = {"firefox": "JSHandle@object"}.get(browser_name, "Window")
+ assert messages[0].text == expected_text
# Upstream issue https://bugs.webkit.org/show_bug.cgi?id=229515
diff --git a/tests/sync/test_element_handle.py b/tests/sync/test_element_handle.py
index c2faa4a6e..3ff5619f6 100644
--- a/tests/sync/test_element_handle.py
+++ b/tests/sync/test_element_handle.py
@@ -362,27 +362,26 @@ def test_hover_when_node_is_removed(page: Page, server: Server) -> None:
assert page.evaluate('document.querySelector("button:hover").id') == "button-6"
-def test_scroll(page: Page, server: Server) -> None:
+@pytest.mark.parametrize("i", range(11))
+def test_scroll(page: Page, server: Server, i: int) -> None:
page.goto(server.PREFIX + "/offscreenbuttons.html")
- for i in range(11):
- button = page.query_selector(f"#btn{i}")
- assert button
- before = button.evaluate(
- """button => {
- return button.getBoundingClientRect().right - window.innerWidth
- }"""
- )
-
- assert before == 10 * i
- button.scroll_into_view_if_needed()
- after = button.evaluate(
- """button => {
- return button.getBoundingClientRect().right - window.innerWidth
- }"""
- )
-
- assert after <= 0
- page.evaluate("() => window.scrollTo(0, 0)")
+ button = page.query_selector(f"#btn{i}")
+ assert button
+ before = button.evaluate(
+ """button => {
+ return button.getBoundingClientRect().right - window.innerWidth
+ }"""
+ )
+
+ assert before == 10 * i
+ button.scroll_into_view_if_needed()
+ after = button.evaluate(
+ """button => {
+ return button.getBoundingClientRect().right - window.innerWidth
+ }"""
+ )
+
+ assert after <= 0
def test_scroll_should_throw_for_detached_element(page: Page) -> None:
diff --git a/tests/sync/test_fetch_browser_context.py b/tests/sync/test_fetch_browser_context.py
index 91390791e..795befedd 100644
--- a/tests/sync/test_fetch_browser_context.py
+++ b/tests/sync/test_fetch_browser_context.py
@@ -15,7 +15,7 @@
import json
import time
from pathlib import Path
-from typing import Any, Dict, List, cast
+from typing import Any, Dict, Generator, List, cast
from urllib.parse import parse_qs
import pytest
@@ -60,24 +60,29 @@ def test_fetch_should_work(context: BrowserContext, server: Server) -> None:
assert response.text() == '{"foo": "bar"}\n'
-def test_should_return_timing(context: BrowserContext) -> None:
- fresh_server = HTTPServer()
- fresh_server.start()
- try:
- response = context.request.get(fresh_server.EMPTY_PAGE)
- timing = response.timing
- assert abs(timing["startTime"] - time.time() * 1000) < 10000
- assert timing["domainLookupStart"] == 0
- assert timing["domainLookupEnd"] >= timing["domainLookupStart"]
- assert timing["connectStart"] == timing["domainLookupEnd"]
- assert timing["secureConnectionStart"] == -1
- assert timing["connectEnd"] >= timing["connectStart"]
- assert timing["requestStart"] == timing["connectEnd"]
- assert timing["responseStart"] >= timing["requestStart"]
- assert timing["responseEnd"] >= timing["responseStart"]
- assert timing["responseEnd"] < 60000
- finally:
- fresh_server.stop()
+@pytest.fixture
+def fresh_server() -> Generator[HTTPServer, None, None]:
+ http_server = HTTPServer()
+ http_server.start()
+ yield http_server
+ http_server.stop()
+
+
+def test_should_return_timing(
+ context: BrowserContext, fresh_server: HTTPServer
+) -> None:
+ response = context.request.get(fresh_server.EMPTY_PAGE)
+ timing = response.timing
+ assert abs(timing["startTime"] - time.time() * 1000) < 10000
+ assert timing["domainLookupStart"] == 0
+ assert timing["domainLookupEnd"] >= timing["domainLookupStart"]
+ assert timing["connectStart"] == timing["domainLookupEnd"]
+ assert timing["secureConnectionStart"] == -1
+ assert timing["connectEnd"] >= timing["connectStart"]
+ assert timing["requestStart"] == timing["connectEnd"]
+ assert timing["responseStart"] >= timing["requestStart"]
+ assert timing["responseEnd"] >= timing["responseStart"]
+ assert timing["responseEnd"] < 60000
def test_should_throw_on_network_error(context: BrowserContext, server: Server) -> None:
diff --git a/tests/sync/test_fetch_global.py b/tests/sync/test_fetch_global.py
index 15a11fca8..ea50bdc53 100644
--- a/tests/sync/test_fetch_global.py
+++ b/tests/sync/test_fetch_global.py
@@ -275,8 +275,10 @@ def test_storage_state_should_round_trip_through_file(
assert state2 == expected
+@pytest.mark.parametrize("method", ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"])
+@pytest.mark.parametrize("max_redirects", [1, 2, 3])
def test_should_throw_an_error_when_max_redirects_is_exceeded(
- playwright: Playwright, server: Server
+ playwright: Playwright, server: Server, method: str, max_redirects: int
) -> None:
server.set_redirect("/a/redirect1", "/b/c/redirect2")
server.set_redirect("/b/c/redirect2", "/b/c/redirect3")
@@ -284,43 +286,40 @@ def test_should_throw_an_error_when_max_redirects_is_exceeded(
server.set_redirect("/b/c/redirect4", "/simple.json")
request = playwright.request.new_context()
- for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]:
- for max_redirects in [1, 2, 3]:
- with pytest.raises(Error) as exc_info:
- request.fetch(
- server.PREFIX + "/a/redirect1",
- method=method,
- max_redirects=max_redirects,
- )
- assert "Max redirect count exceeded" in str(exc_info)
+ with pytest.raises(Error) as exc_info:
+ request.fetch(
+ server.PREFIX + "/a/redirect1",
+ method=method,
+ max_redirects=max_redirects,
+ )
+ assert "Max redirect count exceeded" in str(exc_info)
+@pytest.mark.parametrize("method", ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"])
def test_should_not_follow_redirects_when_max_redirects_is_set_to_0(
- playwright: Playwright, server: Server
+ playwright: Playwright, server: Server, method: str
) -> None:
server.set_redirect("/a/redirect1", "/b/c/redirect2")
server.set_redirect("/b/c/redirect2", "/simple.json")
request = playwright.request.new_context()
- for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]:
- response = request.fetch(
- server.PREFIX + "/a/redirect1", method=method, max_redirects=0
- )
- assert response.headers["location"] == "/b/c/redirect2"
- assert response.status == 302
+ response = request.fetch(
+ server.PREFIX + "/a/redirect1", method=method, max_redirects=0
+ )
+ assert response.headers["location"] == "/b/c/redirect2"
+ assert response.status == 302
+@pytest.mark.parametrize("method", ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"])
def test_should_throw_an_error_when_max_redirects_is_less_than_0(
playwright: Playwright,
server: Server,
+ method: str,
) -> None:
request = playwright.request.new_context()
- for method in ["GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"]:
- with pytest.raises(AssertionError) as exc_info:
- request.fetch(
- server.PREFIX + "/a/redirect1", method=method, max_redirects=-1
- )
- assert "'max_redirects' must be greater than or equal to '0'" in str(exc_info)
+ with pytest.raises(AssertionError) as exc_info:
+ request.fetch(server.PREFIX + "/a/redirect1", method=method, max_redirects=-1)
+ assert "'max_redirects' must be greater than or equal to '0'" in str(exc_info)
def test_should_serialize_null_values_in_json(
diff --git a/tests/sync/test_har.py b/tests/sync/test_har.py
index a401038b6..addd976e8 100644
--- a/tests/sync/test_har.py
+++ b/tests/sync/test_har.py
@@ -82,51 +82,34 @@ def test_should_attach_content(
page.goto(server.PREFIX + "/har.html")
page.evaluate("() => fetch('/pptr.png').then(r => r.arrayBuffer())")
context.close()
+ html_hash = "75841480e2606c03389077304342fac2c58ccb1b"
+ css_hash = "79f739d7bc88e80f55b9891a22bf13a2b4e18adb"
+ png_hash = "a4c3a18f0bb83f5d9fe7ce561e065c36205762fa"
with zipfile.ZipFile(path) as z:
with z.open("har.har") as har:
entries = json.load(har)["log"]["entries"]
- assert "encoding" not in entries[0]["response"]["content"]
- assert (
- entries[0]["response"]["content"]["mimeType"]
- == "text/html; charset=utf-8"
- )
- assert (
- "75841480e2606c03389077304342fac2c58ccb1b"
- in entries[0]["response"]["content"]["_file"]
- )
- assert entries[0]["response"]["content"]["size"] >= 96
- assert entries[0]["response"]["content"]["compression"] == 0
-
- assert "encoding" not in entries[1]["response"]["content"]
- assert (
- entries[1]["response"]["content"]["mimeType"]
- == "text/css; charset=utf-8"
- )
- assert (
- "79f739d7bc88e80f55b9891a22bf13a2b4e18adb"
- in entries[1]["response"]["content"]["_file"]
- )
- assert entries[1]["response"]["content"]["size"] >= 37
- assert entries[1]["response"]["content"]["compression"] == 0
-
- assert "encoding" not in entries[2]["response"]["content"]
- assert entries[2]["response"]["content"]["mimeType"] == "image/png"
- assert (
- "a4c3a18f0bb83f5d9fe7ce561e065c36205762fa"
- in entries[2]["response"]["content"]["_file"]
- )
- assert entries[2]["response"]["content"]["size"] >= 6000
- assert entries[2]["response"]["content"]["compression"] == 0
-
- with z.open("75841480e2606c03389077304342fac2c58ccb1b.html") as f:
- assert b"HAR Page" in f.read()
-
- with z.open("79f739d7bc88e80f55b9891a22bf13a2b4e18adb.css") as f:
- assert b"pink" in f.read()
-
- with z.open("a4c3a18f0bb83f5d9fe7ce561e065c36205762fa.png") as f:
- assert len(f.read()) == entries[2]["response"]["content"]["size"]
+ contents = [entry["response"]["content"] for entry in entries[:3]]
+ assert all("encoding" not in content for content in contents)
+ assert [content["mimeType"] for content in contents] == [
+ "text/html; charset=utf-8",
+ "text/css; charset=utf-8",
+ "image/png",
+ ]
+ files = [content["_file"] for content in contents]
+ assert all(h in f for h, f in zip([html_hash, css_hash, png_hash], files))
+ sizes = [content["size"] for content in contents]
+ assert all(size >= low for size, low in zip(sizes, [96, 37, 6000]))
+ assert [content["compression"] for content in contents] == [0, 0, 0]
+
+ with z.open(f"{html_hash}.html") as f:
+ assert b"HAR Page" in f.read()
+
+ with z.open(f"{css_hash}.css") as f:
+ assert b"pink" in f.read()
+
+ with z.open(f"{png_hash}.png") as f:
+ assert len(f.read()) == contents[2]["size"]
def test_should_include_content(
@@ -258,8 +241,7 @@ def test_by_default_should_abort_requests_not_found_in_har(
context: BrowserContext,
server: Server,
assetdir: Path,
- is_chromium: bool,
- is_webkit: bool,
+ browser_name: str,
) -> None:
context.route_from_har(har=assetdir / "har-fulfill.har")
page = context.new_page()
@@ -267,12 +249,11 @@ def test_by_default_should_abort_requests_not_found_in_har(
with pytest.raises(Error) as exc_info:
page.goto(server.EMPTY_PAGE)
assert exc_info.value
- if is_chromium:
- assert "net::ERR_FAILED" in exc_info.value.message
- elif is_webkit:
- assert "Blocked by Web Inspector" in exc_info.value.message
- else:
- assert "NS_ERROR_FAILURE" in exc_info.value.message
+ expected_message = {
+ "chromium": "net::ERR_FAILED",
+ "webkit": "Blocked by Web Inspector",
+ }.get(browser_name, "NS_ERROR_FAILURE")
+ assert expected_message in exc_info.value.message
def test_fallback_continue_should_continue_requests_on_bad_har(
diff --git a/tests/sync/test_launcher.py b/tests/sync/test_launcher.py
index 6b89b79a8..b719897fd 100644
--- a/tests/sync/test_launcher.py
+++ b/tests/sync/test_launcher.py
@@ -59,16 +59,9 @@ def test_browser_type_executable_path_should_work(
def test_browser_type_name_should_work(
- browser_type: BrowserType, is_webkit: bool, is_firefox: bool, is_chromium: bool
+ browser_type: BrowserType, browser_name: str
) -> None:
- if is_webkit:
- assert browser_type.name == "webkit"
- elif is_firefox:
- assert browser_type.name == "firefox"
- elif is_chromium:
- assert browser_type.name == "chromium"
- else:
- raise ValueError("Unknown browser")
+ assert browser_type.name == browser_name
def test_browser_close_should_fire_close_event_for_all_contexts(
diff --git a/tests/sync/test_locators.py b/tests/sync/test_locators.py
index 1c191f826..04415118b 100644
--- a/tests/sync/test_locators.py
+++ b/tests/sync/test_locators.py
@@ -225,9 +225,7 @@ def test_locators_should_query_existing_elements(page: Page) -> None:
html = page.locator("html")
elements = html.locator("div").element_handles()
assert len(elements) == 2
- result = []
- for element in elements:
- result.append(page.evaluate("e => e.textContent", element))
+ result = [page.evaluate("e => e.textContent", element) for element in elements]
assert result == ["A", "B"]
@@ -339,35 +337,44 @@ def test_locators_should_press(page: Page) -> None:
assert page.eval_on_selector("input", "input => input.value") == "h"
-def test_locators_should_scroll_into_view(page: Page, server: Server) -> None:
+@pytest.mark.parametrize("i", range(11))
+def test_locators_should_scroll_into_view(page: Page, server: Server, i: int) -> None:
page.goto(server.PREFIX + "/offscreenbuttons.html")
- for i in range(11):
- button = page.locator(f"#btn{i}")
- before = button.evaluate(
- "button => button.getBoundingClientRect().right - window.innerWidth"
- )
- assert before == 10 * i
- button.scroll_into_view_if_needed()
- after = button.evaluate(
- "button => button.getBoundingClientRect().right - window.innerWidth"
- )
- assert after <= 0
- page.evaluate("window.scrollTo(0, 0)")
+ button = page.locator(f"#btn{i}")
+ before = button.evaluate(
+ "button => button.getBoundingClientRect().right - window.innerWidth"
+ )
+ assert before == 10 * i
+ button.scroll_into_view_if_needed()
+ after = button.evaluate(
+ "button => button.getBoundingClientRect().right - window.innerWidth"
+ )
+ assert after <= 0
-def test_locators_should_select_textarea(
- page: Page, server: Server, browser_name: str
-) -> None:
+@pytest.fixture
+def selected_textarea(page: Page, server: Server) -> Locator:
page.goto(server.PREFIX + "/input/textarea.html")
textarea = page.locator("textarea")
textarea.evaluate("textarea => textarea.value = 'some value'")
textarea.select_text()
textarea.select_text(timeout=25_000)
- if browser_name == "firefox" or browser_name == "webkit":
- assert textarea.evaluate("el => el.selectionStart") == 0
- assert textarea.evaluate("el => el.selectionEnd") == 10
- else:
- assert page.evaluate("window.getSelection().toString()") == "some value"
+ return textarea
+
+
+@pytest.mark.skip_browser("chromium")
+def test_locators_should_select_textarea_firefox_webkit(
+ selected_textarea: Locator,
+) -> None:
+ assert selected_textarea.evaluate("el => el.selectionStart") == 0
+ assert selected_textarea.evaluate("el => el.selectionEnd") == 10
+
+
+@pytest.mark.only_browser("chromium")
+def test_locators_should_select_textarea_chromium(
+ page: Page, selected_textarea: Locator
+) -> None:
+ assert page.evaluate("window.getSelection().toString()") == "some value"
def test_locators_should_type(page: Page) -> None:
@@ -932,17 +939,22 @@ def test_locators_should_support_locator_and(page: Page) -> None:
).to_have_count(2)
-def test_locators_has_does_not_encode_unicode(page: Page, server: Server) -> None:
+@pytest.mark.parametrize(
+ "make_locator",
+ [
+ lambda page: page.locator("button", has_text="Драматург"),
+ lambda page: page.locator("button", has_text=re.compile("Драматург")),
+ lambda page: page.locator("button", has=page.locator("text=Драматург")),
+ ],
+)
+def test_locators_has_does_not_encode_unicode(
+ page: Page, server: Server, make_locator: Callable[[Page], Locator]
+) -> None:
page.goto(server.EMPTY_PAGE)
- locators = [
- page.locator("button", has_text="Драматург"),
- page.locator("button", has_text=re.compile("Драматург")),
- page.locator("button", has=page.locator("text=Драматург")),
- ]
- for locator in locators:
- with pytest.raises(Error) as exc_info:
- locator.click(timeout=1_000)
- assert "Драматург" in exc_info.value.message
+ locator = make_locator(page)
+ with pytest.raises(Error) as exc_info:
+ locator.click(timeout=1_000)
+ assert "Драматург" in exc_info.value.message
def test_locators_should_focus_and_blur_a_button(page: Page, server: Server) -> None:
@@ -983,9 +995,7 @@ def blur_event() -> None:
def test_locator_all_should_work(page: Page) -> None:
page.set_content("")
- texts = []
- for p in page.locator("p").all():
- texts.append(p.text_content())
+ texts = [p.text_content() for p in page.locator("p").all()]
assert texts == ["A", "B", "C"]
diff --git a/tests/sync/test_page_add_locator_handler.py b/tests/sync/test_page_add_locator_handler.py
index b16aca9f4..05307efc8 100644
--- a/tests/sync/test_page_add_locator_handler.py
+++ b/tests/sync/test_page_add_locator_handler.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import contextlib
import sys
from typing import TYPE_CHECKING
@@ -26,7 +27,21 @@
from playwright.sync_api import Locator as TypeCheckingOnlyLocator
-def test_should_work(page: Page, server: Server) -> None:
+@pytest.mark.parametrize(
+ "args",
+ [
+ ["mouseover", 1],
+ ["mouseover", 1, "capture"],
+ ["mouseover", 2],
+ ["mouseover", 2, "capture"],
+ ["pointerover", 1],
+ ["pointerover", 1, "capture"],
+ ["none", 1],
+ ["remove", 1],
+ ["hide", 1],
+ ],
+)
+def test_should_work(page: Page, server: Server, args: list) -> None:
page.goto(server.PREFIX + "/input/handle-locator.html")
before_count = 0
@@ -44,34 +59,32 @@ def handler(locator: Locator) -> None:
page.add_locator_handler(original_locator, handler)
- for args in [
- ["mouseover", 1],
- ["mouseover", 1, "capture"],
+ page.locator("#aside").hover()
+ page.evaluate(
+ "(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }",
+ args,
+ )
+ assert before_count == 0
+ assert after_count == 0
+ page.locator("#target").click()
+ assert before_count == args[1]
+ assert after_count == args[1]
+ assert page.evaluate("window.clicked") == 1
+ expect(page.locator("#interstitial")).not_to_be_visible()
+
+
+@pytest.mark.parametrize(
+ "args",
+ [
["mouseover", 2],
- ["mouseover", 2, "capture"],
- ["pointerover", 1],
- ["pointerover", 1, "capture"],
["none", 1],
["remove", 1],
["hide", 1],
- ]:
- page.locator("#aside").hover()
- before_count = 0
- after_count = 0
- page.evaluate(
- "(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }",
- args,
- )
- assert before_count == 0
- assert after_count == 0
- page.locator("#target").click()
- assert before_count == args[1]
- assert after_count == args[1]
- assert page.evaluate("window.clicked") == 1
- expect(page.locator("#interstitial")).not_to_be_visible()
-
-
-def test_should_work_with_a_custom_check(page: Page, server: Server) -> None:
+ ],
+)
+def test_should_work_with_a_custom_check(
+ page: Page, server: Server, args: list
+) -> None:
page.goto(server.PREFIX + "/input/handle-locator.html")
def handler() -> None:
@@ -80,20 +93,14 @@ def handler() -> None:
page.add_locator_handler(page.locator("body"), handler, no_wait_after=True)
- for args in [
- ["mouseover", 2],
- ["none", 1],
- ["remove", 1],
- ["hide", 1],
- ]:
- page.locator("#aside").hover()
- page.evaluate(
- "(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }",
- args,
- )
- page.locator("#target").click()
- assert page.evaluate("window.clicked") == 1
- expect(page.locator("#interstitial")).not_to_be_visible()
+ page.locator("#aside").hover()
+ page.evaluate(
+ "(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }",
+ args,
+ )
+ page.locator("#target").click()
+ assert page.evaluate("window.clicked") == 1
+ expect(page.locator("#interstitial")).not_to_be_visible()
def test_should_work_with_keyword_only_handler(page: Page, server: Server) -> None:
@@ -207,10 +214,8 @@ def handler() -> None:
nonlocal called
called += 1
# Deliberately timeout.
- try:
+ with contextlib.suppress(Exception):
page.wait_for_timeout(9999999)
- except Exception:
- pass
page.add_locator_handler(
page.get_by_text("This interstitial covers the button"), handler
diff --git a/tests/sync/test_page_request_fallback.py b/tests/sync/test_page_request_fallback.py
index 53570960c..7fd1bd8c2 100644
--- a/tests/sync/test_page_request_fallback.py
+++ b/tests/sync/test_page_request_fallback.py
@@ -109,9 +109,7 @@ def handler(route: Route) -> None:
assert not failed[0]
-def test_should_not_chain_abort(
- page: Page, server: Server, is_webkit: bool, is_firefox: bool
-) -> None:
+def test_should_not_chain_abort(page: Page, server: Server, browser_name: str) -> None:
failed: List[bool] = [False]
def handler(route: Route) -> None:
@@ -123,12 +121,11 @@ def handler(route: Route) -> None:
with pytest.raises(Error) as excinfo:
page.goto(server.EMPTY_PAGE)
- if is_webkit:
- assert "Blocked by Web Inspector" in excinfo.value.message
- elif is_firefox:
- assert "NS_ERROR_FAILURE" in excinfo.value.message
- else:
- assert "net::ERR_FAILED" in excinfo.value.message
+ expected_message = {
+ "webkit": "Blocked by Web Inspector",
+ "firefox": "NS_ERROR_FAILURE",
+ }.get(browser_name, "net::ERR_FAILED")
+ assert expected_message in excinfo.value.message
assert not failed[0]
diff --git a/tests/sync/test_queryselector.py b/tests/sync/test_queryselector.py
index 27b972e95..ce40c8cbd 100644
--- a/tests/sync/test_queryselector.py
+++ b/tests/sync/test_queryselector.py
@@ -75,9 +75,8 @@ def test_selectors_register_should_work_with_path(
assert page.eval_on_selector("foo=whatever", "e => e.nodeName") == "SECTION"
-def test_selectors_register_should_work_in_main_and_isolated_world(
- selectors: Selectors, page: Page, utils: Utils
-) -> None:
+@pytest.fixture
+def main_and_isolated_engines(selectors: Selectors, page: Page, utils: Utils) -> None:
dummy_selector_script = """{
create(root, target) { },
query(root, selector) {
@@ -94,6 +93,10 @@ def test_selectors_register_should_work_in_main_and_isolated_world(
)
page.set_content("
")
page.evaluate('() => window.__answer = document.querySelector("span")')
+
+
+@pytest.mark.usefixtures("main_and_isolated_engines")
+def test_selectors_register_should_work_in_main_world(page: Page) -> None:
# Works in main if asked.
assert page.eval_on_selector("main=ignored", "e => e.nodeName") == "SPAN"
assert page.eval_on_selector("css=div >> main=ignored", "e => e.nodeName") == "SPAN"
@@ -103,6 +106,10 @@ def test_selectors_register_should_work_in_main_and_isolated_world(
assert (
page.eval_on_selector_all("main=ignored", "es => es.filter(e => e).length") == 3
)
+
+
+@pytest.mark.usefixtures("main_and_isolated_engines")
+def test_selectors_register_should_work_in_isolated_world(page: Page) -> None:
# Works in isolated by default.
assert page.query_selector("isolated=ignored") is None
assert page.query_selector("css=div >> isolated=ignored") is None
@@ -114,6 +121,10 @@ def test_selectors_register_should_work_in_main_and_isolated_world(
page.eval_on_selector_all("isolated=ignored", "es => es.filter(e => e).length")
== 3
)
+
+
+@pytest.mark.usefixtures("main_and_isolated_engines")
+def test_selectors_register_should_chain_main_and_isolated_engines(page: Page) -> None:
# At least one engine in main forces all to be in main.
assert (
page.eval_on_selector("main=ignored >> isolated=ignored", "e => e.nodeName")
diff --git a/tests/sync/test_screencast.py b/tests/sync/test_screencast.py
index 06ae46704..95d2b02cf 100644
--- a/tests/sync/test_screencast.py
+++ b/tests/sync/test_screencast.py
@@ -20,14 +20,20 @@
from tests.server import Server
-def ensure_some_frames(page: Page) -> None:
- for _ in range(100):
+def ensure_some_frames(page: Page, count: int = 100) -> None:
+ for _ in range(count):
page.evaluate(
"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))"
)
page.screenshot()
+def wait_for_received_frames(page: Page, received: list, timeout: float = 10) -> None:
+ deadline = time.time() + timeout
+ while not received and time.time() < deadline:
+ page.wait_for_timeout(100)
+
+
def test_should_expose_screencast_property(page: Page) -> None:
assert page.screencast is page.screencast
@@ -39,14 +45,8 @@ def test_start_should_deliver_frames_via_callback(page: Page, server: Server) ->
page.evaluate("() => document.body.style.backgroundColor = 'red'")
# Force a couple of paint cycles so engines that only emit on visual change
# still produce a frame. Mirrors upstream `ensureSomeFrames`.
- for _ in range(3):
- page.evaluate(
- "() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))"
- )
- page.screenshot()
- deadline = time.time() + 10
- while not received and time.time() < deadline:
- page.wait_for_timeout(100)
+ ensure_some_frames(page, count=3)
+ wait_for_received_frames(page, received)
page.screencast.stop()
assert len(received) >= 1
assert all(isinstance(d, bytes) and len(d) > 0 for d in received)
@@ -60,8 +60,7 @@ def on_frame(_: object) -> None:
frame_count += 1
assert page.title() == ""
- page.screencast.start(on_frame=on_frame)
- try:
+ with page.screencast.start(on_frame=on_frame):
page.goto(server.EMPTY_PAGE)
page.evaluate("() => document.body.style.backgroundColor = 'red'")
ensure_some_frames(page)
@@ -69,8 +68,6 @@ def on_frame(_: object) -> None:
page.evaluate("() => document.body.style.backgroundColor = 'blue'")
ensure_some_frames(page)
assert frame_count > frames_before_repaint
- finally:
- page.screencast.stop()
def test_should_report_callback_errors_and_continue_delivering_frames(
@@ -116,12 +113,9 @@ def on_frame(_: object) -> None:
def test_starting_twice_should_throw(page: Page) -> None:
- page.screencast.start(on_frame=lambda f: None)
- try:
+ with page.screencast.start(on_frame=lambda f: None):
with pytest.raises(Exception, match="already started"):
page.screencast.start(on_frame=lambda f: None)
- finally:
- page.screencast.stop()
def test_on_frame_receives_viewport_size(browser: Browser, server: Server) -> None:
@@ -133,25 +127,18 @@ def test_on_frame_receives_viewport_size(browser: Browser, server: Server) -> No
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.evaluate("() => document.body.style.backgroundColor = 'red'")
- for _ in range(100):
- page.evaluate(
- "() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))"
- )
- page.screenshot()
+ ensure_some_frames(page)
page.screencast.stop()
assert len(received) >= 1
assert any(frame["viewportWidth"] == 1000 for frame in received)
- for frame in received:
- assert frame["viewportHeight"] == 400
- assert isinstance(frame["timestamp"], (int, float))
+ assert all(frame["viewportHeight"] == 400 for frame in received)
+ assert all(isinstance(frame["timestamp"], (int, float)) for frame in received)
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
- page.screencast.start(on_frame=lambda f: None)
- try:
+ page.set_content("")
+ with page.screencast.start(on_frame=lambda f: None):
with page.screencast.show_actions(duration=100, cursor="pointer"):
- pass
+ page.locator("button").click()
with page.screencast.show_actions(duration=100, cursor="none"):
- pass
- finally:
- page.screencast.stop()
+ page.locator("button").click()
diff --git a/tests/sync/test_sync.py b/tests/sync/test_sync.py
index 7e3d36977..c7b669538 100644
--- a/tests/sync/test_sync.py
+++ b/tests/sync/test_sync.py
@@ -144,9 +144,10 @@ def test_sync_handle_multiple_pages(context: BrowserContext) -> None:
assert len(context.pages) == 1
page2.close()
assert len(context.pages) == 0
- for page in [page1, page2]:
- with pytest.raises(Error):
- page.content()
+ with pytest.raises(Error):
+ page1.content()
+ with pytest.raises(Error):
+ page2.content()
def test_sync_wait_for_event(page: Page, server: Server) -> None:
@@ -205,12 +206,11 @@ def test_console_should_work(page: Page, browser_name: str) -> None:
page.evaluate('() => console.log("hello", 5, {foo: "bar"})')
assert len(messages) == 1
message = messages[0]
- if browser_name != "firefox":
- assert message.text == "hello 5 {foo: bar}"
- assert str(message) == "hello 5 {foo: bar}"
- else:
- assert message.text == "hello 5 JSHandle@object"
- assert str(message) == "hello 5 JSHandle@object"
+ expected_text = {"firefox": "hello 5 JSHandle@object"}.get(
+ browser_name, "hello 5 {foo: bar}"
+ )
+ assert message.text == expected_text
+ assert str(message) == expected_text
assert message.type == "log"
assert message.args[0].json_value() == "hello"
assert message.args[1].json_value() == 5
@@ -354,15 +354,10 @@ def test_call_sync_method_after_playwright_close_with_own_loop(
def test_should_return_proper_api_name_on_error(page: Page) -> None:
- try:
+ with pytest.raises(Error) as exc_info:
page.evaluate("does_not_exist")
-
- assert (
- False
- ), "Accessing undefined JavaScript variable should have thrown exception"
- except Exception as error:
- # Each browser returns slightly different error messages, but they should all start with "Page.evaluate:", because that was the Playwright method where the error originated
- assert str(error).startswith("Page.evaluate:")
+ # Each browser returns slightly different error messages, but they should all start with "Page.evaluate:", because that was the Playwright method where the error originated
+ assert str(exc_info.value).startswith("Page.evaluate:")
def test_should_not_orphan_callback_on_non_serializable_params(