diff --git a/DRIVER_VERSION b/DRIVER_VERSION
index e95c523d9..97c65529f 100644
--- a/DRIVER_VERSION
+++ b/DRIVER_VERSION
@@ -1 +1 @@
-b335271cdc2b57cac2be8ad37c5dd283ac6b8938
+6e2c003f16e01822c3ea45bc894c2542a6e98189
diff --git a/NODE_VERSION b/NODE_VERSION
index a43a5b943..ce62013c3 100644
--- a/NODE_VERSION
+++ b/NODE_VERSION
@@ -1 +1 @@
-24.22.0
+24.22.1
diff --git a/README.md b/README.md
index 174f26bc6..fd83daf67 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
| :--- | :---: | :---: | :---: |
| Chromium 156.0.8078.12 | ✅ | ✅ | ✅ |
| WebKit 27.2 | ✅ | ✅ | ✅ |
-| Firefox 157.0 | ✅ | ✅ | ✅ |
+| Firefox 158.0 | ✅ | ✅ | ✅ |
## Documentation
diff --git a/playwright/_impl/_protocol.py b/playwright/_impl/_protocol.py
index 90aac2129..3e0d7384c 100644
--- a/playwright/_impl/_protocol.py
+++ b/playwright/_impl/_protocol.py
@@ -2207,7 +2207,12 @@
},
"webStorageClear": {"kind": {"type": "enum", "literals": ["local", "session"]}},
},
- "Root": {"initialize": {"sdkLanguage": "SDKLanguage"}},
+ "Root": {
+ "initialize": {
+ "sdkLanguage": "SDKLanguage",
+ "supportsLoneSurrogates": "boolean?",
+ }
+ },
"Playwright": {
"newRequest": {
"baseURL": "string?",
diff --git a/playwright/_impl/browser_context.py b/playwright/_impl/browser_context.py
index db9ea750b..2ac048522 100644
--- a/playwright/_impl/browser_context.py
+++ b/playwright/_impl/browser_context.py
@@ -35,6 +35,7 @@
import base64
import json
import os
+import re
from pathlib import Path
from typing import (
TYPE_CHECKING,
@@ -324,7 +325,9 @@ async def _initialize_har_from_options(
if not record_har_path:
return
default_content: Literal["attach", "embed"] = (
- "attach" if str(record_har_path).endswith(".zip") else "embed"
+ "attach"
+ if re.search(r"\.zip$", str(record_har_path), re.IGNORECASE)
+ else "embed"
)
await self.tracing._record_into_har(
har=record_har_path,
diff --git a/playwright/_impl/connection.py b/playwright/_impl/connection.py
index 845f403d3..6c387f940 100644
--- a/playwright/_impl/connection.py
+++ b/playwright/_impl/connection.py
@@ -83,6 +83,9 @@ async def initialize(self) -> "Playwright":
await self._channel.initialize(
# Python: the SDK language of this client.
sdkLanguage="python",
+ # json.dumps() escapes a lone surrogate as \udxxx, and json.loads()
+ # reads it back, as the JS client does.
+ supportsLoneSurrogates=True,
timeout=NO_TIMEOUT,
)
)["playwright"]
diff --git a/playwright/_impl/protocol/validator.py b/playwright/_impl/protocol/validator.py
index b16749396..c8f925af7 100644
--- a/playwright/_impl/protocol/validator.py
+++ b/playwright/_impl/protocol/validator.py
@@ -4181,6 +4181,7 @@
scheme["RootInitializeParams"] = t_object(
{
"sdkLanguage": t_type("SDKLanguage"),
+ "supportsLoneSurrogates": t_optional(t_boolean),
}
)
scheme["RootInitializeResult"] = t_object(
diff --git a/playwright/_impl/protocol_channels.py b/playwright/_impl/protocol_channels.py
index e73c755c4..94220c522 100644
--- a/playwright/_impl/protocol_channels.py
+++ b/playwright/_impl/protocol_channels.py
@@ -4952,11 +4952,15 @@ class RootChannel(Channel):
def initialize(
self,
sdkLanguage: channels.SDKLanguage,
+ supportsLoneSurrogates: Optional[bool],
timeout: float = 0,
) -> Awaitable[channels.RootInitializeResult]:
return self._call(
"initialize",
- {"sdkLanguage": sdkLanguage},
+ {
+ "sdkLanguage": sdkLanguage,
+ "supportsLoneSurrogates": supportsLoneSurrogates,
+ },
timeout,
lambda result: {"playwright": result["playwright"]},
is_internal=True,
diff --git a/playwright/_impl/tracing.py b/playwright/_impl/tracing.py
index 969caff67..8453bedf9 100644
--- a/playwright/_impl/tracing.py
+++ b/playwright/_impl/tracing.py
@@ -192,7 +192,7 @@ async def start_har(
# Not ported: the check of the resourcesDir option against a .zip har file;
# the option is JavaScript only.
default_content: Literal["attach", "embed"] = (
- "attach" if str(path).endswith(".zip") else "embed"
+ "attach" if re.search(r"\.zip$", str(path), re.IGNORECASE) else "embed"
)
self._har_id = await self._record_into_har(
har=path,
@@ -222,7 +222,7 @@ async def _record_into_har(
# Not ported: the resourcesDir option; it is JavaScript only.
# Python: the path can be a pathlib.Path.
har = str(har)
- is_zip = har.endswith(".zip")
+ is_zip = bool(re.search(r"\.zip$", har, re.IGNORECASE))
options: Dict[str, Any] = {
"content": update_content if update_content is not None else "attach",
"urlGlob": url if isinstance(url, str) else None,
@@ -250,7 +250,7 @@ async def _export_har(self, har_id: str) -> None:
return
del self._har_recorders[har_id]
is_local = not self._connection.is_remote()
- is_zip = har_params["path"].endswith(".zip")
+ is_zip = bool(re.search(r"\.zip$", har_params["path"], re.IGNORECASE))
if is_local:
entries = (
diff --git a/tests/async/library/test_browsercontext_har.py b/tests/async/library/test_browsercontext_har.py
index dde1a8a25..39683ad12 100644
--- a/tests/async/library/test_browsercontext_har.py
+++ b/tests/async/library/test_browsercontext_har.py
@@ -292,7 +292,7 @@ async def test_HA_085_should_round_trip_har_zip(
server: Server,
tmp_path: Path,
) -> None:
- har_path = tmp_path / "har.zip"
+ har_path = tmp_path / "har.ZIP"
context1 = await context_factory(
record_har_mode="minimal", record_har_path=har_path
)
@@ -300,6 +300,12 @@ async def test_HA_085_should_round_trip_har_zip(
await page1.goto(server.PREFIX + "/one-style.html")
await context1.close()
+ har_dir = tmp_path / "har"
+ with zipfile.ZipFile(har_path) as zip_file:
+ zip_file.extractall(har_dir)
+ log = json.loads((har_dir / "har.har").read_text(encoding="utf-8"))["log"]
+ assert isinstance(log["entries"][0]["response"]["content"]["_file"], str)
+
context2 = await context_factory()
await context2.route_from_har(har_path, not_found="abort")
page2 = await context2.new_page()
diff --git a/tests/async/library/test_browsercontext_locale.py b/tests/async/library/test_browsercontext_locale.py
index 8004d3dc5..f9cf75768 100644
--- a/tests/async/library/test_browsercontext_locale.py
+++ b/tests/async/library/test_browsercontext_locale.py
@@ -183,18 +183,33 @@ async def test_LC_010_should_propagate_locale_to_workers(
context = await browser.new_context(locale="ru-RU")
page = await context.new_page()
await page.goto(server.EMPTY_PAGE)
- async with page.expect_console_message(
- lambda e: e.text.startswith("locale:")
- ) as msg_info:
- await page.evaluate(
- """() => new Worker(URL.createObjectURL(new Blob(['console.log("locale:" + Intl.NumberFormat().resolvedOptions().locale)'], { type: 'application/javascript' })))"""
+ async with (
+ page.expect_console_message(lambda e: e.text.startswith("locale:")) as msg_info,
+ page.expect_console_message(
+ lambda e: e.text.startswith("languages:")
+ ) as languages_info,
+ ):
+ request, _ = await asyncio.gather(
+ server.wait_for_request("/title.html"),
+ page.evaluate(
+ """url => new Worker(URL.createObjectURL(new Blob([`
+ console.log("locale:" + Intl.NumberFormat().resolvedOptions().locale);
+ console.log("languages:" + navigator.languages);
+ fetch(${JSON.stringify(url)});
+ `], { type: 'application/javascript' })))""",
+ server.PREFIX + "/title.html",
+ ),
)
msg = await msg_info.value
+ languages = await languages_info.value
if browser_name == "webkit":
# Webkit on Ubuntu is "ru-RU", and on other platforms is "ru"
assert "locale:ru" in msg.text
+ assert "languages:ru" in languages.text
else:
assert msg.text == "locale:ru-RU"
+ assert languages.text == "languages:ru-RU"
+ assert "ru-RU" in (request.getHeader("accept-language") or "")
await context.close()
diff --git a/tests/async/library/test_browsercontext_storage_state.py b/tests/async/library/test_browsercontext_storage_state.py
index dec2415ae..e07326ea3 100644
--- a/tests/async/library/test_browsercontext_storage_state.py
+++ b/tests/async/library/test_browsercontext_storage_state.py
@@ -433,12 +433,6 @@ async def test_ST_010_should_handle_malformed_file(
) in str(exc_info.value)
-@pytest.mark.xfail(
- reason="driver: it replaces lone surrogates with U+FFFD in the messages to the "
- "clients other than JavaScript (toWellFormed() in cli/driver.ts), "
- "https://github.com/microsoft/playwright/issues/43542",
- strict=True,
-)
async def test_ST_011_should_serialize_storage_state_with_lone_surrogates(
page: Page, context: BrowserContext, server: Server
) -> None:
diff --git a/tests/async/library/test_har.py b/tests/async/library/test_har.py
index 7451940b0..6196d9455 100644
--- a/tests/async/library/test_har.py
+++ b/tests/async/library/test_har.py
@@ -516,6 +516,34 @@ async def test_HA_021_should_include_content_smoke(
assert content["compression"] == 0
+async def test_HA_113_should_store_binary_content_with_a_textual_mime_type_as_base64(
+ page_with_har: PageWithHar, server: Server, browser_name: str
+) -> None:
+ if browser_name == "webkit":
+ pytest.skip(
+ "fixme: WebKit decodes the body of a textual resource before it gets to us"
+ )
+ body = bytes([0x80, 0xC3, 0x28, 0xFF, 0x41])
+
+ def handle(request: TestServerRequest) -> None:
+ request.setHeader("Content-Type", "application/javascript")
+ request.write(body)
+ request.finish()
+
+ server.set_route("/binary.js", handle)
+ har = await page_with_har()
+ await har.page.goto(server.EMPTY_PAGE)
+ await har.page.evaluate("() => fetch('/binary.js').then(r => r.arrayBuffer())")
+ log = await har.get_log()
+ content = next(
+ entry
+ for entry in log["entries"]
+ if entry["request"]["url"].endswith("/binary.js")
+ )["response"]["content"]
+ assert content["encoding"] == "base64"
+ assert base64.b64decode(content["text"]) == body
+
+
async def test_HA_022_should_use_attach_mode_for_zip_extension(
page_with_har: PageWithHar, server: Server
) -> None:
diff --git a/tests/async/page/test_page_drag.py b/tests/async/page/test_page_drag.py
index 44c732714..e54f7d4e5 100644
--- a/tests/async/page/test_page_drag.py
+++ b/tests/async/page/test_page_drag.py
@@ -278,10 +278,6 @@ async def test_DD_008_drag_and_drop_should_respect_the_drop_effect(
is_mac: bool,
headless: bool,
) -> None:
- if browser_name == "webkit" and not is_linux:
- pytest.skip(
- "fixme: WebKit doesn't handle the drop effect correctly outside of linux."
- )
if browser_name == "webkit" and is_linux and not headless:
pytest.skip("fixme: https://github.com/microsoft/playwright/issues/21646")
if browser_name == "chromium" and not is_mac and not headless:
@@ -340,6 +336,43 @@ async def test_if_dropped(effect_allowed: str, drop_effect: str) -> Any:
assert await test_if_dropped("uninitialized", "copy") is True
+async def test_DD_028_drag_and_drop_should_report_the_default_drop_effect_from_effect_allowed(
+ page: Page, browser_name: str, is_linux: bool, headless: bool
+) -> None:
+ if browser_name == "webkit" and is_linux and not headless:
+ pytest.skip("fixme: https://github.com/microsoft/playwright/issues/21646")
+
+ await page.set_content(
+ """
+
drag target
+ this is the drop target
+ """
+ )
+ await page.evaluate(
+ """() => {
+ window['dropEffect'] = 'none';
+
+ document.querySelector('div').addEventListener('dragstart', event => {
+ event.dataTransfer.effectAllowed = 'copyMove';
+ event.dataTransfer.setData('text/plain', 'drag data');
+ });
+ document.querySelector('div').addEventListener('dragend', event => {
+ window['dropEffect'] = event.dataTransfer.dropEffect;
+ });
+
+ // The target accepts the drag without setting dropEffect.
+ document.querySelector('drop-target').addEventListener('dragover', event => {
+ event.preventDefault();
+ });
+ }"""
+ )
+ await page.hover("div")
+ await page.mouse.down()
+ await page.hover("drop-target")
+ await page.mouse.up()
+ assert await page.evaluate("dropEffect") == "move"
+
+
async def test_DD_009_drag_and_drop_should_work_if_the_drag_is_canceled(
page: Page, server: Server
) -> None:
diff --git a/tests/async/page/test_page_fill.py b/tests/async/page/test_page_fill.py
index 722e2ae89..13ea7d86e 100644
--- a/tests/async/page/test_page_fill.py
+++ b/tests/async/page/test_page_fill.py
@@ -412,12 +412,15 @@ async def test_FI_025_should_be_able_to_fill_when_focus_is_in_the_wrong_frame(
await page.set_content(
"""
-
+
"""
)
await page.focus("iframe")
await page.fill("div", "some value")
assert await page.eval_on_selector("div", "d => d.textContent") == "some value"
+ paragraph = page.frame_locator("iframe").locator("p")
+ await paragraph.fill("new")
+ await expect(paragraph).to_have_text("new")
async def test_FI_026_should_be_able_to_fill_the_input_type_number(
diff --git a/tests/async/page/test_page_mouse.py b/tests/async/page/test_page_mouse.py
index 29cdb0532..27996fce8 100644
--- a/tests/async/page/test_page_mouse.py
+++ b/tests/async/page/test_page_mouse.py
@@ -359,17 +359,17 @@ async def test_MO_012_should_tween_mouse_movement(
"""() => {
window['result'] = [];
document.addEventListener('mousemove', event => {
- window['result'].push([event.clientX, event.clientY]);
+ window['result'].push([event.clientX, event.clientY, event.movementX, event.movementY]);
});
}"""
)
await page.mouse.move(200, 300, steps=5)
assert await page.evaluate("result") == [
- [120, 140],
- [140, 180],
- [160, 220],
- [180, 260],
- [200, 300],
+ [120, 140, 20, 40],
+ [140, 180, 20, 40],
+ [160, 220, 20, 40],
+ [180, 260, 20, 40],
+ [200, 300, 20, 40],
]
diff --git a/tests/async/page/test_page_route.py b/tests/async/page/test_page_route.py
index 80550def3..baa67e81b 100644
--- a/tests/async/page/test_page_route.py
+++ b/tests/async/page/test_page_route.py
@@ -1220,16 +1220,27 @@ async def test_RO_044_should_support_cors_for_different_methods(
async def test_RO_045_should_support_the_times_parameter_with_route_matching(
page: Page, server: Server
) -> None:
+ await page.goto(server.EMPTY_PAGE)
+
+ async def fetch_body() -> str:
+ return await page.evaluate(
+ "() => fetch('/empty.html').then(response => response.text())"
+ )
+
intercepted: List[int] = []
async def handler(route: Route) -> None:
intercepted.append(1)
- await route.continue_()
+ await route.fulfill(body="first")
await page.route("**/empty.html", handler, times=1)
- await page.goto(server.EMPTY_PAGE)
- await page.goto(server.EMPTY_PAGE)
- await page.goto(server.EMPTY_PAGE)
+ assert await fetch_body() == "first"
+ await page.route(
+ "**/empty.html", lambda route: route.fulfill(body="second"), times=1
+ )
+ assert await fetch_body() == "second"
+ await fetch_body()
+ await fetch_body()
assert len(intercepted) == 1
diff --git a/tests/async/page/test_page_screenshot.py b/tests/async/page/test_page_screenshot.py
index 1a2d20f5b..aefdbd546 100644
--- a/tests/async/page/test_page_screenshot.py
+++ b/tests/async/page/test_page_screenshot.py
@@ -505,6 +505,44 @@ async def test_SS_025_page_screenshot_should_work_for_webgl(
assert_to_be_golden(screenshot, "screenshot-webgl.png")
+@pytest.mark.usefixtures("skip_firefox_headed")
+async def test_SS_095_page_screenshot_should_capture_webgl_canvas_after_its_drawing_buffer_is_resized(
+ page: Page, browser_name: str, platform: str, utils: Utils
+) -> None:
+ if (
+ browser_name == "webkit"
+ and platform == "darwin"
+ and os.uname().machine == "x86_64"
+ ):
+ pytest.skip(
+ "WebGL is not available on Intel macOS - https://bugs.webkit.org/show_bug.cgi?id=278277"
+ )
+
+ await page.set_viewport_size({"width": 200, "height": 200})
+ await page.set_content(
+ ''
+ )
+ expected = await page.screenshot()
+
+ await page.set_content(
+ """"""
+ )
+ await utils.rafraf(page)
+ await page.evaluate("() => window.draw(50)")
+ await utils.rafraf(page)
+ assert compare_images(await page.screenshot(), expected) is None
+
+
@pytest.mark.usefixtures("skip_firefox_headed")
async def test_SS_026_page_screenshot_should_work_for_translate_z(
page: Page, server: Server, assert_to_be_golden: GoldenAssert
diff --git a/tests/sync/library/test_browsercontext_har.py b/tests/sync/library/test_browsercontext_har.py
index 986c29b47..79c56d6f0 100644
--- a/tests/sync/library/test_browsercontext_har.py
+++ b/tests/sync/library/test_browsercontext_har.py
@@ -280,12 +280,18 @@ def test_HA_085_should_round_trip_har_zip(
server: Server,
tmp_path: Path,
) -> None:
- har_path = tmp_path / "har.zip"
+ har_path = tmp_path / "har.ZIP"
context1 = context_factory(record_har_mode="minimal", record_har_path=har_path)
page1 = context1.new_page()
page1.goto(server.PREFIX + "/one-style.html")
context1.close()
+ har_dir = tmp_path / "har"
+ with zipfile.ZipFile(har_path) as zip_file:
+ zip_file.extractall(har_dir)
+ log = json.loads((har_dir / "har.har").read_text(encoding="utf-8"))["log"]
+ assert isinstance(log["entries"][0]["response"]["content"]["_file"], str)
+
context2 = context_factory()
context2.route_from_har(har_path, not_found="abort")
page2 = context2.new_page()
diff --git a/tests/sync/library/test_browsercontext_locale.py b/tests/sync/library/test_browsercontext_locale.py
index c764dd843..c11bbb4e3 100644
--- a/tests/sync/library/test_browsercontext_locale.py
+++ b/tests/sync/library/test_browsercontext_locale.py
@@ -188,18 +188,32 @@ def test_LC_010_should_propagate_locale_to_workers(
context = browser.new_context(locale="ru-RU")
page = context.new_page()
page.goto(server.EMPTY_PAGE)
- with page.expect_console_message(
- lambda e: e.text.startswith("locale:")
- ) as msg_info:
+ with (
+ page.expect_console_message(lambda e: e.text.startswith("locale:")) as msg_info,
+ page.expect_console_message(
+ lambda e: e.text.startswith("languages:")
+ ) as languages_info,
+ server.expect_request("/title.html") as request_info,
+ ):
page.evaluate(
- """() => new Worker(URL.createObjectURL(new Blob(['console.log("locale:" + Intl.NumberFormat().resolvedOptions().locale)'], { type: 'application/javascript' })))"""
+ """url => new Worker(URL.createObjectURL(new Blob([`
+ console.log("locale:" + Intl.NumberFormat().resolvedOptions().locale);
+ console.log("languages:" + navigator.languages);
+ fetch(${JSON.stringify(url)});
+ `], { type: 'application/javascript' })))""",
+ server.PREFIX + "/title.html",
)
msg = msg_info.value
+ languages = languages_info.value
+ request = wait_for_server_request(page, request_info)
if browser_name == "webkit":
# Webkit on Ubuntu is "ru-RU", and on other platforms is "ru"
assert "locale:ru" in msg.text
+ assert "languages:ru" in languages.text
else:
assert msg.text == "locale:ru-RU"
+ assert languages.text == "languages:ru-RU"
+ assert "ru-RU" in (request.getHeader("accept-language") or "")
context.close()
diff --git a/tests/sync/library/test_browsercontext_storage_state.py b/tests/sync/library/test_browsercontext_storage_state.py
index d97a5e9c9..7c53b4d9a 100644
--- a/tests/sync/library/test_browsercontext_storage_state.py
+++ b/tests/sync/library/test_browsercontext_storage_state.py
@@ -433,12 +433,6 @@ def test_ST_010_should_handle_malformed_file(
) in str(exc_info.value)
-@pytest.mark.xfail(
- reason="driver: it replaces lone surrogates with U+FFFD in the messages to the "
- "clients other than JavaScript (toWellFormed() in cli/driver.ts), "
- "https://github.com/microsoft/playwright/issues/43542",
- strict=True,
-)
def test_ST_011_should_serialize_storage_state_with_lone_surrogates(
page: Page, context: BrowserContext, server: Server
) -> None:
diff --git a/tests/sync/library/test_har.py b/tests/sync/library/test_har.py
index ac0e740a6..19d352804 100644
--- a/tests/sync/library/test_har.py
+++ b/tests/sync/library/test_har.py
@@ -515,6 +515,34 @@ def test_HA_021_should_include_content_smoke(
assert content["compression"] == 0
+def test_HA_113_should_store_binary_content_with_a_textual_mime_type_as_base64(
+ page_with_har: PageWithHar, server: Server, browser_name: str
+) -> None:
+ if browser_name == "webkit":
+ pytest.skip(
+ "fixme: WebKit decodes the body of a textual resource before it gets to us"
+ )
+ body = bytes([0x80, 0xC3, 0x28, 0xFF, 0x41])
+
+ def handle(request: TestServerRequest) -> None:
+ request.setHeader("Content-Type", "application/javascript")
+ request.write(body)
+ request.finish()
+
+ server.set_route("/binary.js", handle)
+ har = page_with_har()
+ har.page.goto(server.EMPTY_PAGE)
+ har.page.evaluate("() => fetch('/binary.js').then(r => r.arrayBuffer())")
+ log = har.get_log()
+ content = next(
+ entry
+ for entry in log["entries"]
+ if entry["request"]["url"].endswith("/binary.js")
+ )["response"]["content"]
+ assert content["encoding"] == "base64"
+ assert base64.b64decode(content["text"]) == body
+
+
def test_HA_022_should_use_attach_mode_for_zip_extension(
page_with_har: PageWithHar, server: Server
) -> None:
diff --git a/tests/sync/page/test_page_drag.py b/tests/sync/page/test_page_drag.py
index 4257f056f..e0029d112 100644
--- a/tests/sync/page/test_page_drag.py
+++ b/tests/sync/page/test_page_drag.py
@@ -273,10 +273,6 @@ def test_DD_008_drag_and_drop_should_respect_the_drop_effect(
is_mac: bool,
headless: bool,
) -> None:
- if browser_name == "webkit" and not is_linux:
- pytest.skip(
- "fixme: WebKit doesn't handle the drop effect correctly outside of linux."
- )
if browser_name == "webkit" and is_linux and not headless:
pytest.skip("fixme: https://github.com/microsoft/playwright/issues/21646")
if browser_name == "chromium" and not is_mac and not headless:
@@ -335,6 +331,43 @@ def test_if_dropped(effect_allowed: str, drop_effect: str) -> Any:
assert test_if_dropped("uninitialized", "copy") is True
+def test_DD_028_drag_and_drop_should_report_the_default_drop_effect_from_effect_allowed(
+ page: Page, browser_name: str, is_linux: bool, headless: bool
+) -> None:
+ if browser_name == "webkit" and is_linux and not headless:
+ pytest.skip("fixme: https://github.com/microsoft/playwright/issues/21646")
+
+ page.set_content(
+ """
+ drag target
+ this is the drop target
+ """
+ )
+ page.evaluate(
+ """() => {
+ window['dropEffect'] = 'none';
+
+ document.querySelector('div').addEventListener('dragstart', event => {
+ event.dataTransfer.effectAllowed = 'copyMove';
+ event.dataTransfer.setData('text/plain', 'drag data');
+ });
+ document.querySelector('div').addEventListener('dragend', event => {
+ window['dropEffect'] = event.dataTransfer.dropEffect;
+ });
+
+ // The target accepts the drag without setting dropEffect.
+ document.querySelector('drop-target').addEventListener('dragover', event => {
+ event.preventDefault();
+ });
+ }"""
+ )
+ page.hover("div")
+ page.mouse.down()
+ page.hover("drop-target")
+ page.mouse.up()
+ assert page.evaluate("dropEffect") == "move"
+
+
def test_DD_009_drag_and_drop_should_work_if_the_drag_is_canceled(
page: Page, server: Server
) -> None:
diff --git a/tests/sync/page/test_page_fill.py b/tests/sync/page/test_page_fill.py
index 2365592d5..535d62520 100644
--- a/tests/sync/page/test_page_fill.py
+++ b/tests/sync/page/test_page_fill.py
@@ -402,12 +402,15 @@ def test_FI_025_should_be_able_to_fill_when_focus_is_in_the_wrong_frame(
page.set_content(
"""
-
+
"""
)
page.focus("iframe")
page.fill("div", "some value")
assert page.eval_on_selector("div", "d => d.textContent") == "some value"
+ paragraph = page.frame_locator("iframe").locator("p")
+ paragraph.fill("new")
+ expect(paragraph).to_have_text("new")
def test_FI_026_should_be_able_to_fill_the_input_type_number(
diff --git a/tests/sync/page/test_page_mouse.py b/tests/sync/page/test_page_mouse.py
index 518e0d6e5..3b8c6ecac 100644
--- a/tests/sync/page/test_page_mouse.py
+++ b/tests/sync/page/test_page_mouse.py
@@ -348,17 +348,17 @@ def test_MO_012_should_tween_mouse_movement(
"""() => {
window['result'] = [];
document.addEventListener('mousemove', event => {
- window['result'].push([event.clientX, event.clientY]);
+ window['result'].push([event.clientX, event.clientY, event.movementX, event.movementY]);
});
}"""
)
page.mouse.move(200, 300, steps=5)
assert page.evaluate("result") == [
- [120, 140],
- [140, 180],
- [160, 220],
- [180, 260],
- [200, 300],
+ [120, 140, 20, 40],
+ [140, 180, 20, 40],
+ [160, 220, 20, 40],
+ [180, 260, 20, 40],
+ [200, 300, 20, 40],
]
diff --git a/tests/sync/page/test_page_route.py b/tests/sync/page/test_page_route.py
index de2293a92..7a10557a2 100644
--- a/tests/sync/page/test_page_route.py
+++ b/tests/sync/page/test_page_route.py
@@ -1201,16 +1201,25 @@ def test_RO_044_should_support_cors_for_different_methods(
def test_RO_045_should_support_the_times_parameter_with_route_matching(
page: Page, server: Server
) -> None:
+ page.goto(server.EMPTY_PAGE)
+
+ def fetch_body() -> str:
+ return page.evaluate(
+ "() => fetch('/empty.html').then(response => response.text())"
+ )
+
intercepted: List[int] = []
def handler(route: Route) -> None:
intercepted.append(1)
- route.continue_()
+ route.fulfill(body="first")
page.route("**/empty.html", handler, times=1)
- page.goto(server.EMPTY_PAGE)
- page.goto(server.EMPTY_PAGE)
- page.goto(server.EMPTY_PAGE)
+ assert fetch_body() == "first"
+ page.route("**/empty.html", lambda route: route.fulfill(body="second"), times=1)
+ assert fetch_body() == "second"
+ fetch_body()
+ fetch_body()
assert len(intercepted) == 1
diff --git a/tests/sync/page/test_page_screenshot.py b/tests/sync/page/test_page_screenshot.py
index 3d7e434ba..200d4317e 100644
--- a/tests/sync/page/test_page_screenshot.py
+++ b/tests/sync/page/test_page_screenshot.py
@@ -505,6 +505,44 @@ def test_SS_025_page_screenshot_should_work_for_webgl(
assert_to_be_golden(screenshot, "screenshot-webgl.png")
+@pytest.mark.usefixtures("skip_firefox_headed")
+def test_SS_095_page_screenshot_should_capture_webgl_canvas_after_its_drawing_buffer_is_resized(
+ page: Page, browser_name: str, platform: str, utils: Utils
+) -> None:
+ if (
+ browser_name == "webkit"
+ and platform == "darwin"
+ and os.uname().machine == "x86_64"
+ ):
+ pytest.skip(
+ "WebGL is not available on Intel macOS - https://bugs.webkit.org/show_bug.cgi?id=278277"
+ )
+
+ page.set_viewport_size({"width": 200, "height": 200})
+ page.set_content(
+ ''
+ )
+ expected = page.screenshot()
+
+ page.set_content(
+ """"""
+ )
+ utils.rafraf(page)
+ page.evaluate("() => window.draw(50)")
+ utils.rafraf(page)
+ assert compare_images(page.screenshot(), expected) is None
+
+
@pytest.mark.usefixtures("skip_firefox_headed")
def test_SS_026_page_screenshot_should_work_for_translate_z(
page: Page, server: Server, assert_to_be_golden: GoldenAssert
diff --git a/tests/upstream_registry.md b/tests/upstream_registry.md
index 442c8c679..ad2c6a1c5 100644
--- a/tests/upstream_registry.md
+++ b/tests/upstream_registry.md
@@ -26,108 +26,106 @@ subsystem, add a row to the Subsystems table. Then update this file:
## Subsystems
-98 subsystems, 4933 tests (5330 with loop copies).
-
-| Code | Subsystem | Files | Tests |
-|---|---|---|---|
-| `AN` | Android WebViews | `library/unit/android-webviews` | 1 |
-| `AR` | Aria snapshot | `page/{page-aria-snapshot, page-aria-snapshot-ai, page-aria-snapshot-json, to-match-aria-snapshot}`
`library/role-utils` | 170 |
-| `AT` | Add script and style tags | `page/{page-add-script-tag, page-add-style-tag}` | 20 |
-| `AU` | Authentication | `library/{browsercontext-credentials, browsercontext-webauthn}` | 24 |
-| `AW` | Auto-waiting | `page/{page-autowaiting-basic, page-autowaiting-no-hang}` | 23 |
-| `BC` | BrowserContext | `library/{browsercontext-basic, browsercontext-events, browsercontext-pages, browsercontext-page-event, browsercontext-reuse}` | 83 |
-| `BP` | Browsers path | `library/browsers-path` | 2 |
-| `BR` | Browser | `library/{browser, browser-server}` | 10 |
-| `BT` | BrowserType | `library/{browsertype-basic, browsertype-launch, launcher, browsertype-launch-server, browsertype-connect, browsertype-launch-selenium}` | 103 |
-| `BU` | Before unload | `library/beforeunload` | 12 |
-| `CA` | Capabilities | `library/{capabilities, modernizr}` | 31 |
-| `CC` | Client certificates | `library/client-certificates` | 41 |
-| `CD` | CDP | `library/chromium/{connect-over-cdp, session}` | 52 |
-| `CF` | Code frame | `library/unit/code-frame` | 13 |
-| `CG` | Codegen | `library/inspector/{cli-codegen-1, cli-codegen-2, cli-codegen-3, cli-codegen-aria, cli-codegen-pick-locator, cli-codegen-javascript, cli-codegen-test, cli-codegen-python, cli-codegen-python-async, cli-codegen-pytest, cli-codegen-java, cli-codegen-csharp}`
`library/unit/codegen` | 191 |
-| `CH` | Check | `page/page-check` | 17 |
-| `CK` | Clock | `library/{page-clock, page-clock.frozen}`
`library/unit/clock` | 219 |
-| `CL` | Click | `page/{page-click, page-click-scroll, page-click-react, page-click-during-navigation, page-click-timeout-1, page-click-timeout-2, page-click-timeout-3, page-click-timeout-4}` | 122 |
-| `CM` | Console messages | `page/page-event-console` | 20 |
-| `CN` | Channels | `library/channels` | 6 |
-| `CO` | Cookies | `library/{browsercontext-cookies, browsercontext-add-cookies, browsercontext-clearcookies, browsercontext-cookies-third-party}` | 62 |
-| `CR` | Chromium | `library/chromium/{chromium, launcher, extensions, bfcache, connect-to-worker, disable-web-security}` | 45 |
-| `CS` | CSP | `library/browsercontext-csp` | 5 |
-| `CV` | Coverage | `library/chromium/{js-coverage, css-coverage}`
`library/coverage` | 30 |
-| `DB` | Debug | `library/{debugger, debug-controller}` | 22 |
-| `DC` | Default (persistent) context | `library/{defaultbrowsercontext-1, defaultbrowsercontext-2}` | 42 |
-| `DD` | Drag and drop | `page/{page-drag, page-drop}` | 27 |
-| `DE` | Dispatch event | `page/page-dispatchevent` | 17 |
-| `DI` | Dialog | `page/page-dialog` | 13 |
-| `DO` | Download | `library/{download, downloads-path}` | 44 |
-| `DV` | Device | `library/{browsercontext-device, browsercontext-dsf, browsercontext-viewport-mobile}` | 26 |
-| `EE` | Event emitter | `page/page-listeners`
`library/events/{add-listeners, check-listener-leaks, events-list, listener-count, listeners-side-effects, listeners, max-listeners, method-names, modify-in-emit, num-args, once, prepend, remove-all-listeners-wait, remove-all-listeners, remove-listeners, set-max-listeners-side-effects, special-event-names, subclass, symbols}` | 56 |
-| `EF` | Expose function | `page/page-expose-function`
`library/browsercontext-expose-function` | 28 |
-| `EH` | ElementHandle | `page/{elementhandle-bounding-box, elementhandle-click, elementhandle-content-frame, elementhandle-convenience, elementhandle-eval-on-selector, elementhandle-misc, elementhandle-owner-frame, elementhandle-press, elementhandle-query-selector, elementhandle-screenshot, elementhandle-scroll-into-view, elementhandle-select-text, elementhandle-type, elementhandle-wait-for-element-state}` | 123 |
-| `EM` | Emulation | `page/page-emulate-media`
`library/{emulation-focus, emulation-coop}` | 27 |
-| `ER` | Page errors and crashes | `page/page-event-pageerror`
`library/page-event-crash` | 21 |
-| `ES` | Eval on selector | `page/{eval-on-selector, eval-on-selector-all}` | 35 |
-| `EV` | Evaluate | `page/{page-evaluate, page-evaluate-handle, page-evaluate-callback, page-evaluate-no-stall}` | 137 |
-| `EX` | Expect | `page/{expect-boolean, expect-builtins, expect-matcher-result, expect-misc, expect-timeout, expect-to-have-accessible, expect-to-have-text, expect-to-have-value, expect-with-snapshot, matchers.misc}` | 328 |
-| `FA` | Favicon | `library/favicon` | 1 |
-| `FC` | File chooser | `page/page-filechooser` | 28 |
-| `FE` | Fetch | `library/{global-fetch, global-fetch-cookie, browsercontext-fetch, browsercontext-fetch-algorithms, browsercontext-fetch-happy-eyeballs}` | 188 |
-| `FF` | Firefox | `library/firefox/launcher`
`page/firefox-network-response` | 8 |
-| `FI` | Fill | `page/page-fill` | 36 |
-| `FO` | Focus | `page/page-focus` | 12 |
-| `FR` | Frame | `page/{frame-evaluate, frame-frame-element, frame-goto, frame-hierarchy}` | 36 |
-| `GE` | Geolocation | `library/geolocation` | 9 |
-| `GN` | Locator and selector generator | `library/{locator-generator, selector-generator}` | 104 |
-| `HA` | HAR | `library/{har, browsercontext-har, har-websocket}` | 112 |
-| `HE` | Headful | `library/headful` | 16 |
-| `HT` | Hit target | `library/hit-target` | 22 |
-| `IF` | Input files | `page/page-set-input-files` | 21 |
-| `IN` | Inspector | `library/inspector/{pause, recorder-api, console-api, title}` | 58 |
-| `IS` | Init script | `page/{page-add-init-script, page-add-init-script-callback}`
`library/browsercontext-add-init-script` | 35 |
-| `JH` | JSHandle | `page/{jshandle-as-element, jshandle-evaluate, jshandle-json-value, jshandle-properties, jshandle-to-string}` | 23 |
-| `JS` | JSON schema | `library/unit/json-schema` | 10 |
-| `KB` | Keyboard | `page/page-keyboard` | 53 |
-| `LC` | Locale | `library/browsercontext-locale` | 14 |
-| `LE` | Leaks | `page/page-leaks`
`library/heap` | 14 |
-| `LG` | Logger | `library/logger` | 2 |
-| `LH` | Locator handler | `page/page-add-locator-handler` | 14 |
-| `LO` | Locator | `page/{locator-any-frame, locator-click, locator-convenience, locator-element-handle, locator-evaluate, locator-frame, locator-highlight, locator-is-visible, locator-list, locator-misc-1, locator-misc-2, locator-query, locator-wait-for-function}`
`library/locator-highlight` | 199 |
-| `MC` | Multiple clients | `library/multiclient` | 19 |
-| `MO` | Mouse | `page/{page-mouse, wheel}` | 23 |
-| `NA` | Navigation | `page/{page-goto, page-navigation, page-history, page-event-load}`
`library/browsercontext-base-url` | 96 |
-| `NE` | Network | `page/{page-network-request, page-network-response, page-network-sizes, page-network-idle, page-event-network, page-event-request, network-post-data, page-set-extra-http-headers}`
`library/{browsercontext-set-extra-http-headers, browsercontext-network-event, network-timeout, resource-timing, ignorehttpserrors}` | 142 |
-| `OO` | OOPIF | `library/chromium/oopif` | 29 |
-| `PA` | Page | `page/{page-basic, page-set-content, page-transition}`
`library/page-close` | 56 |
-| `PC` | Playwright client | `library/playwright-client` | 1 |
-| `PD` | PDF | `library/pdf` | 2 |
-| `PE` | Permissions | `library/permissions` | 23 |
-| `PO` | Popup | `page/page-event-popup`
`library/popup` | 28 |
-| `PR` | Proxy | `library/{proxy, browsercontext-proxy, proxy-pattern, fetch-proxy}` | 47 |
-| `PS` | Parser | `library/{locator-parser, component-parser, css-parser}` | 51 |
-| `QS` | Query selector | `page/queryselector` | 17 |
-| `RE` | Retarget | `page/retarget` | 15 |
-| `RO` | Route | `page/{page-route, page-request-continue, page-request-fulfill, page-request-fallback, page-request-intercept, page-request-gc, interception}`
`library/{browsercontext-route, unroute-behavior}` | 201 |
-| `SC` | Screencast | `library/{screencast, screencast-actions, screencast-overlay}` | 38 |
-| `SE` | Selectors | `page/{selectors-css, selectors-text, selectors-role, selectors-get-by, selectors-frame, selectors-misc, selectors-register}`
`library/selectors-register` | 167 |
-| `SI` | Signals | `library/signals` | 10 |
-| `SL` | Slow motion | `library/slowmo` | 39 |
-| `SM` | Strict mode | `page/page-strict`
`library/browsercontext-strict` | 14 |
-| `SO` | Select option | `page/page-select-option` | 33 |
-| `SQ` | Sequence | `library/unit/sequence` | 16 |
-| `SS` | Screenshot | `page/page-screenshot`
`library/screenshot` | 94 |
-| `ST` | Storage | `library/browsercontext-storage-state`
`page/{page-localstorage, page-cache-storage}` | 32 |
-| `TA` | Tap | `library/{tap, locator-dispatchevent-touch}` | 10 |
-| `TO` | Timeout runner | `library/unit/timeout-runner` | 1 |
-| `TR` | Tracing | `library/tracing`
`library/chromium/tracing` | 49 |
-| `TV` | Trace viewer | `library/{trace-viewer, snapshot-renderer}` | 135 |
-| `TZ` | Timezone | `library/browsercontext-timezone-id` | 6 |
-| `UA` | User agent | `library/browsercontext-user-agent` | 6 |
-| `VI` | Video | `library/video` | 31 |
-| `VP` | Viewport | `library/browsercontext-viewport` | 17 |
-| `WA` | Wait for | `page/{page-wait-for-function, page-wait-for-selector-1, page-wait-for-selector-2, page-wait-for-load-state, page-wait-for-navigation, page-wait-for-url, page-wait-for-request, page-wait-for-response}` | 136 |
-| `WM` | WebMCP | `library/{webmcp, webmcp-disabled}` | 15 |
-| `WO` | Workers | `page/workers`
`library/{shared-worker, browsercontext-service-worker-policy}` | 31 |
-| `WS` | WebSocket | `library/{web-socket, route-web-socket}` | 40 |
+| Code | Subsystem | Files |
+|---|---|---|
+| `AN` | Android WebViews | `library/unit/android-webviews` |
+| `AR` | Aria snapshot | `page/{page-aria-snapshot, page-aria-snapshot-ai, page-aria-snapshot-json, to-match-aria-snapshot}`
`library/role-utils` |
+| `AT` | Add script and style tags | `page/{page-add-script-tag, page-add-style-tag}` |
+| `AU` | Authentication | `library/{browsercontext-credentials, browsercontext-webauthn}` |
+| `AW` | Auto-waiting | `page/{page-autowaiting-basic, page-autowaiting-no-hang}` |
+| `BC` | BrowserContext | `library/{browsercontext-basic, browsercontext-events, browsercontext-pages, browsercontext-page-event, browsercontext-reuse}` |
+| `BP` | Browsers path | `library/browsers-path` |
+| `BR` | Browser | `library/{browser, browser-server}` |
+| `BT` | BrowserType | `library/{browsertype-basic, browsertype-launch, launcher, browsertype-launch-server, browsertype-connect, browsertype-launch-selenium}` |
+| `BU` | Before unload | `library/beforeunload` |
+| `CA` | Capabilities | `library/{capabilities, modernizr}` |
+| `CC` | Client certificates | `library/client-certificates` |
+| `CD` | CDP | `library/chromium/{connect-over-cdp, session}` |
+| `CF` | Code frame | `library/unit/code-frame` |
+| `CG` | Codegen | `library/inspector/{cli-codegen-1, cli-codegen-2, cli-codegen-3, cli-codegen-aria, cli-codegen-pick-locator, cli-codegen-javascript, cli-codegen-test, cli-codegen-python, cli-codegen-python-async, cli-codegen-pytest, cli-codegen-java, cli-codegen-csharp}`
`library/unit/codegen` |
+| `CH` | Check | `page/page-check` |
+| `CK` | Clock | `library/{page-clock, page-clock.frozen}`
`library/unit/clock` |
+| `CL` | Click | `page/{page-click, page-click-scroll, page-click-react, page-click-during-navigation, page-click-timeout-1, page-click-timeout-2, page-click-timeout-3, page-click-timeout-4}` |
+| `CM` | Console messages | `page/page-event-console` |
+| `CN` | Channels | `library/channels` |
+| `CO` | Cookies | `library/{browsercontext-cookies, browsercontext-add-cookies, browsercontext-clearcookies, browsercontext-cookies-third-party}` |
+| `CR` | Chromium | `library/chromium/{chromium, launcher, extensions, bfcache, connect-to-worker, disable-web-security}` |
+| `CS` | CSP | `library/browsercontext-csp` |
+| `CV` | Coverage | `library/chromium/{js-coverage, css-coverage}`
`library/coverage` |
+| `DB` | Debug | `library/{debugger, debug-controller}` |
+| `DC` | Default (persistent) context | `library/{defaultbrowsercontext-1, defaultbrowsercontext-2}` |
+| `DD` | Drag and drop | `page/{page-drag, page-drop}` |
+| `DE` | Dispatch event | `page/page-dispatchevent` |
+| `DI` | Dialog | `page/page-dialog` |
+| `DO` | Download | `library/{download, downloads-path}` |
+| `DV` | Device | `library/{browsercontext-device, browsercontext-dsf, browsercontext-viewport-mobile}` |
+| `EE` | Event emitter | `page/page-listeners`
`library/events/{add-listeners, check-listener-leaks, events-list, listener-count, listeners-side-effects, listeners, max-listeners, method-names, modify-in-emit, num-args, once, prepend, remove-all-listeners-wait, remove-all-listeners, remove-listeners, set-max-listeners-side-effects, special-event-names, subclass, symbols}` |
+| `EF` | Expose function | `page/page-expose-function`
`library/browsercontext-expose-function` |
+| `EH` | ElementHandle | `page/{elementhandle-bounding-box, elementhandle-click, elementhandle-content-frame, elementhandle-convenience, elementhandle-eval-on-selector, elementhandle-misc, elementhandle-owner-frame, elementhandle-press, elementhandle-query-selector, elementhandle-screenshot, elementhandle-scroll-into-view, elementhandle-select-text, elementhandle-type, elementhandle-wait-for-element-state}` |
+| `EM` | Emulation | `page/page-emulate-media`
`library/{emulation-focus, emulation-coop}` |
+| `ER` | Page errors and crashes | `page/page-event-pageerror`
`library/page-event-crash` |
+| `ES` | Eval on selector | `page/{eval-on-selector, eval-on-selector-all}` |
+| `EV` | Evaluate | `page/{page-evaluate, page-evaluate-handle, page-evaluate-callback, page-evaluate-no-stall}` |
+| `EX` | Expect | `page/{expect-boolean, expect-builtins, expect-matcher-result, expect-misc, expect-timeout, expect-to-have-accessible, expect-to-have-text, expect-to-have-value, expect-with-snapshot, matchers.misc}` |
+| `FA` | Favicon | `library/favicon` |
+| `FC` | File chooser | `page/page-filechooser` |
+| `FE` | Fetch | `library/{global-fetch, global-fetch-cookie, browsercontext-fetch, browsercontext-fetch-algorithms, browsercontext-fetch-happy-eyeballs}` |
+| `FF` | Firefox | `library/firefox/launcher`
`page/firefox-network-response` |
+| `FI` | Fill | `page/page-fill` |
+| `FO` | Focus | `page/page-focus` |
+| `FR` | Frame | `page/{frame-evaluate, frame-frame-element, frame-goto, frame-hierarchy}` |
+| `GE` | Geolocation | `library/geolocation` |
+| `GN` | Locator and selector generator | `library/{locator-generator, selector-generator}` |
+| `HA` | HAR | `library/{har, browsercontext-har, har-websocket}` |
+| `HE` | Headful | `library/headful` |
+| `HT` | Hit target | `library/hit-target` |
+| `IF` | Input files | `page/page-set-input-files` |
+| `IN` | Inspector | `library/inspector/{pause, recorder-api, console-api, title}` |
+| `IS` | Init script | `page/{page-add-init-script, page-add-init-script-callback}`
`library/browsercontext-add-init-script` |
+| `JH` | JSHandle | `page/{jshandle-as-element, jshandle-evaluate, jshandle-json-value, jshandle-properties, jshandle-to-string}` |
+| `JS` | JSON schema | `library/unit/json-schema` |
+| `KB` | Keyboard | `page/page-keyboard` |
+| `LC` | Locale | `library/browsercontext-locale` |
+| `LE` | Leaks | `page/page-leaks`
`library/heap` |
+| `LG` | Logger | `library/logger` |
+| `LH` | Locator handler | `page/page-add-locator-handler` |
+| `LO` | Locator | `page/{locator-any-frame, locator-click, locator-convenience, locator-element-handle, locator-evaluate, locator-frame, locator-highlight, locator-is-visible, locator-list, locator-misc-1, locator-misc-2, locator-query, locator-wait-for-function}`
`library/locator-highlight` |
+| `MC` | Multiple clients | `library/multiclient` |
+| `MO` | Mouse | `page/{page-mouse, wheel}` |
+| `NA` | Navigation | `page/{page-goto, page-navigation, page-history, page-event-load}`
`library/browsercontext-base-url` |
+| `NE` | Network | `page/{page-network-request, page-network-response, page-network-sizes, page-network-idle, page-event-network, page-event-request, network-post-data, page-set-extra-http-headers}`
`library/{browsercontext-set-extra-http-headers, browsercontext-network-event, network-timeout, resource-timing, ignorehttpserrors}` |
+| `OO` | OOPIF | `library/chromium/oopif` |
+| `PA` | Page | `page/{page-basic, page-set-content, page-transition}`
`library/page-close` |
+| `PC` | Playwright client | `library/playwright-client` |
+| `PD` | PDF | `library/pdf` |
+| `PE` | Permissions | `library/permissions` |
+| `PO` | Popup | `page/page-event-popup`
`library/popup` |
+| `PR` | Proxy | `library/{proxy, browsercontext-proxy, proxy-pattern, fetch-proxy}` |
+| `PS` | Parser | `library/{locator-parser, component-parser, css-parser}` |
+| `QS` | Query selector | `page/queryselector` |
+| `RE` | Retarget | `page/retarget` |
+| `RO` | Route | `page/{page-route, page-request-continue, page-request-fulfill, page-request-fallback, page-request-intercept, page-request-gc, interception}`
`library/{browsercontext-route, unroute-behavior}` |
+| `SC` | Screencast | `library/{screencast, screencast-actions, screencast-overlay}` |
+| `SE` | Selectors | `page/{selectors-css, selectors-text, selectors-role, selectors-get-by, selectors-frame, selectors-misc, selectors-register}`
`library/selectors-register` |
+| `SI` | Signals | `library/signals` |
+| `SL` | Slow motion | `library/slowmo` |
+| `SM` | Strict mode | `page/page-strict`
`library/browsercontext-strict` |
+| `SO` | Select option | `page/page-select-option` |
+| `SQ` | Sequence | `library/unit/sequence` |
+| `SS` | Screenshot | `page/page-screenshot`
`library/screenshot` |
+| `ST` | Storage | `library/browsercontext-storage-state`
`page/{page-localstorage, page-cache-storage}` |
+| `TA` | Tap | `library/{tap, locator-dispatchevent-touch}` |
+| `TO` | Timeout runner | `library/unit/timeout-runner` |
+| `TR` | Tracing | `library/tracing`
`library/chromium/tracing` |
+| `TV` | Trace viewer | `library/{trace-viewer, snapshot-renderer}` |
+| `TZ` | Timezone | `library/browsercontext-timezone-id` |
+| `UA` | User agent | `library/browsercontext-user-agent` |
+| `VI` | Video | `library/video` |
+| `VP` | Viewport | `library/browsercontext-viewport` |
+| `WA` | Wait for | `page/{page-wait-for-function, page-wait-for-selector-1, page-wait-for-selector-2, page-wait-for-load-state, page-wait-for-navigation, page-wait-for-url, page-wait-for-request, page-wait-for-response}` |
+| `WM` | WebMCP | `library/{webmcp, webmcp-disabled}` |
+| `WO` | Workers | `page/workers`
`library/{shared-worker, browsercontext-service-worker-policy}` |
+| `WS` | WebSocket | `library/{web-socket, route-web-socket}` |
## Tests
@@ -1658,6 +1656,7 @@ subsystem, add a row to the Subsystems table. Then update this file:
| `DD-025` | `page/page-drop.spec.ts` | should drop files and data together |
| `DD-026` | `page/page-drop.spec.ts` | should throw when target does not accept drop |
| `DD-027` | `page/page-drop.spec.ts` | should throw when neither files nor data provided |
+| `DD-028` | `page/page-drag.spec.ts` | Drag and drop › should report the default drop effect from effectAllowed |
### DE — Dispatch event
@@ -3157,6 +3156,7 @@ subsystem, add a row to the Subsystems table. Then update this file:
| `HA-110` | `library/har-websocket.spec.ts` | should still allow routeWebSocket to fully mock the connection when capturing HAR |
| `HA-111` | `library/har-websocket.spec.ts` | should still allow routeWebSocket to modify messages when capturing HAR |
| `HA-112` | `library/har-websocket.spec.ts` | should respect PLAYWRIGHT\_HAR\_NO\_WEBSOCKET\_FRAMES |
+| `HA-113` | `library/har.spec.ts` | should store binary content with a textual mime type as base64 |
### HE — Headful
@@ -4979,6 +4979,7 @@ subsystem, add a row to the Subsystems table. Then update this file:
| `SS-092` | `library/screenshot.spec.ts` | element screenshot › should capture full element when larger than viewport with device scale factor |
| `SS-093` | `library/screenshot.spec.ts` | element screenshot › should capture full element when larger than viewport with device scale factor and scale:css |
| `SS-094` | `library/screenshot.spec.ts` | element screenshot › page screenshot should capture css transform with device pixels |
+| `SS-095` | `page/page-screenshot.spec.ts` | page screenshot › should capture webgl canvas after its drawing buffer is resized |
### ST — Storage