Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DRIVER_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a1f35e1955976e84411f9c10358228a80b763ebe
3b438229c6a2ccdbe2d3572755935892203d83c3
5 changes: 5 additions & 0 deletions playwright/_impl/_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@
},
},
"strictSelectors": "boolean?",
"locateOnlyVisible": "boolean?",
"serviceWorkers": {"type": "enum?", "literals": ["allow", "block"]},
"selectorEngines": {"type": "array?", "items": "SelectorEngine"},
"testIdAttributeName": "string?",
Expand Down Expand Up @@ -821,6 +822,7 @@
},
},
"strictSelectors": "boolean?",
"locateOnlyVisible": "boolean?",
"serviceWorkers": {"type": "enum?", "literals": ["allow", "block"]},
"selectorEngines": {"type": "array?", "items": "SelectorEngine"},
"testIdAttributeName": "string?",
Expand Down Expand Up @@ -949,6 +951,7 @@
},
},
"strictSelectors": "boolean?",
"locateOnlyVisible": "boolean?",
"serviceWorkers": {"type": "enum?", "literals": ["allow", "block"]},
"selectorEngines": {"type": "array?", "items": "SelectorEngine"},
"testIdAttributeName": "string?",
Expand Down Expand Up @@ -1285,6 +1288,7 @@
},
},
"strictSelectors": "boolean?",
"locateOnlyVisible": "boolean?",
"serviceWorkers": {"type": "enum?", "literals": ["allow", "block"]},
"selectorEngines": {"type": "array?", "items": "SelectorEngine"},
"testIdAttributeName": "string?",
Expand Down Expand Up @@ -1375,6 +1379,7 @@
},
},
"strictSelectors": "boolean?",
"locateOnlyVisible": "boolean?",
"timezoneId": "string?",
"tracesDir": "string?",
"artifactsDir": "string?",
Expand Down
2 changes: 2 additions & 0 deletions playwright/_impl/browser_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,8 @@ async def prepare_browser_context_params(options: Dict[str, Any]) -> Dict[str, A
else None
),
"strictSelectors": options.get("strict_selectors"),
# Python: locateOnlyVisible is JavaScript-only (docs: langs: js).
"locateOnlyVisible": None,
"selectorEngines": options.get("selectorEngines"),
"testIdAttributeName": options.get("testIdAttributeName"),
"proxy": options.get("proxy"),
Expand Down
2 changes: 2 additions & 0 deletions playwright/_impl/locator.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def __init__(
# Not ported: util.inspect.custom is Node.js only; __repr__() is the Python
# counterpart.

# Not ported: selectorForQuery() supports locateOnlyVisible (langs: js).

def _wrap_api_call(
self,
func: Callable[[ApiZone], Awaitable[R]],
Expand Down
6 changes: 6 additions & 0 deletions playwright/_impl/protocol/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"serviceWorkers": t_optional(t_enum(["allow", "block"])),
"selectorEngines": t_optional(t_array(t_type("SelectorEngine"))),
"testIdAttributeName": t_optional(t_string),
Expand Down Expand Up @@ -783,6 +784,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"serviceWorkers": t_optional(t_enum(["allow", "block"])),
"selectorEngines": t_optional(t_array(t_type("SelectorEngine"))),
"testIdAttributeName": t_optional(t_string),
Expand Down Expand Up @@ -923,6 +925,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"serviceWorkers": t_optional(t_enum(["allow", "block"])),
"selectorEngines": t_optional(t_array(t_type("SelectorEngine"))),
"testIdAttributeName": t_optional(t_string),
Expand Down Expand Up @@ -1100,6 +1103,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"serviceWorkers": t_optional(t_enum(["allow", "block"])),
"selectorEngines": t_optional(t_array(t_type("SelectorEngine"))),
"testIdAttributeName": t_optional(t_string),
Expand Down Expand Up @@ -1717,6 +1721,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"serviceWorkers": t_optional(t_enum(["allow", "block"])),
"selectorEngines": t_optional(t_array(t_type("SelectorEngine"))),
"testIdAttributeName": t_optional(t_string),
Expand Down Expand Up @@ -1869,6 +1874,7 @@
)
),
"strictSelectors": t_optional(t_boolean),
"locateOnlyVisible": t_optional(t_boolean),
"timezoneId": t_optional(t_string),
"tracesDir": t_optional(t_string),
"artifactsDir": t_optional(t_string),
Expand Down
10 changes: 10 additions & 0 deletions playwright/_impl/protocol_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ def launchBrowser(
baseURL: Optional[str],
recordVideo: Optional[channels.AndroidDeviceLaunchBrowserParamsRecordVideo],
strictSelectors: Optional[bool],
locateOnlyVisible: Optional[bool],
serviceWorkers: Optional[Literal["allow", "block"]],
selectorEngines: Optional[Sequence[channels.SelectorEngine]],
testIdAttributeName: Optional[str],
Expand Down Expand Up @@ -369,6 +370,7 @@ def launchBrowser(
"baseURL": baseURL,
"recordVideo": recordVideo,
"strictSelectors": strictSelectors,
"locateOnlyVisible": locateOnlyVisible,
"serviceWorkers": serviceWorkers,
"selectorEngines": selectorEngines,
"testIdAttributeName": testIdAttributeName,
Expand Down Expand Up @@ -816,6 +818,7 @@ def newContext(
baseURL: Optional[str],
recordVideo: Optional[channels.BrowserNewContextParamsRecordVideo],
strictSelectors: Optional[bool],
locateOnlyVisible: Optional[bool],
serviceWorkers: Optional[Literal["allow", "block"]],
selectorEngines: Optional[Sequence[channels.SelectorEngine]],
testIdAttributeName: Optional[str],
Expand Down Expand Up @@ -852,6 +855,7 @@ def newContext(
"baseURL": baseURL,
"recordVideo": recordVideo,
"strictSelectors": strictSelectors,
"locateOnlyVisible": locateOnlyVisible,
"serviceWorkers": serviceWorkers,
"selectorEngines": selectorEngines,
"testIdAttributeName": testIdAttributeName,
Expand Down Expand Up @@ -894,6 +898,7 @@ def newContextForReuse(
baseURL: Optional[str],
recordVideo: Optional[channels.BrowserNewContextForReuseParamsRecordVideo],
strictSelectors: Optional[bool],
locateOnlyVisible: Optional[bool],
serviceWorkers: Optional[Literal["allow", "block"]],
selectorEngines: Optional[Sequence[channels.SelectorEngine]],
testIdAttributeName: Optional[str],
Expand Down Expand Up @@ -930,6 +935,7 @@ def newContextForReuse(
"baseURL": baseURL,
"recordVideo": recordVideo,
"strictSelectors": strictSelectors,
"locateOnlyVisible": locateOnlyVisible,
"serviceWorkers": serviceWorkers,
"selectorEngines": selectorEngines,
"testIdAttributeName": testIdAttributeName,
Expand Down Expand Up @@ -1804,6 +1810,7 @@ def launchPersistentContext(
channels.BrowserTypeLaunchPersistentContextParamsRecordVideo
],
strictSelectors: Optional[bool],
locateOnlyVisible: Optional[bool],
serviceWorkers: Optional[Literal["allow", "block"]],
selectorEngines: Optional[Sequence[channels.SelectorEngine]],
testIdAttributeName: Optional[str],
Expand Down Expand Up @@ -1857,6 +1864,7 @@ def launchPersistentContext(
"baseURL": baseURL,
"recordVideo": recordVideo,
"strictSelectors": strictSelectors,
"locateOnlyVisible": locateOnlyVisible,
"serviceWorkers": serviceWorkers,
"selectorEngines": selectorEngines,
"testIdAttributeName": testIdAttributeName,
Expand Down Expand Up @@ -1940,6 +1948,7 @@ def launch(
offline: Optional[bool],
recordVideo: Optional[channels.ElectronLaunchParamsRecordVideo],
strictSelectors: Optional[bool],
locateOnlyVisible: Optional[bool],
timezoneId: Optional[str],
tracesDir: Optional[str],
artifactsDir: Optional[str],
Expand All @@ -1966,6 +1975,7 @@ def launch(
"offline": offline,
"recordVideo": recordVideo,
"strictSelectors": strictSelectors,
"locateOnlyVisible": locateOnlyVisible,
"timezoneId": timezoneId,
"tracesDir": tracesDir,
"artifactsDir": artifactsDir,
Expand Down
1 change: 1 addition & 0 deletions playwright/_impl/protocol_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ class BrowserContextInitializerOptions(TypedDict):
baseURL: NotRequired[str]
recordVideo: NotRequired[BrowserContextInitializerOptionsRecordVideo]
strictSelectors: NotRequired[bool]
locateOnlyVisible: NotRequired[bool]
serviceWorkers: NotRequired[Literal["allow", "block"]]
selectorEngines: NotRequired[List[SelectorEngine]]
testIdAttributeName: NotRequired[str]
Expand Down
8 changes: 8 additions & 0 deletions playwright/async_api/_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -7958,6 +7958,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -9893,6 +9895,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -11846,6 +11850,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -17784,6 +17790,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down
8 changes: 8 additions & 0 deletions playwright/sync_api/_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -7920,6 +7920,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -9891,6 +9893,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -11874,6 +11878,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down Expand Up @@ -17949,6 +17955,8 @@ def get_by_label(
Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the
`aria-label` attribute.

Associated `<label>` elements are also supported for form-associated custom elements.

**Usage**

For example, this method will find inputs by label \"Username\" and \"Password\" in the following DOM:
Expand Down
4 changes: 4 additions & 0 deletions tests/assets/extension-mv3-simple/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
// Mock script for background extension
globalThis.initialTimezone = {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
localDate: new Date('2030-01-01T23:00').toISOString(),
};
globalThis.MAGIC = 42;
22 changes: 21 additions & 1 deletion tests/async/library/chromium/test_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

import asyncio
import re
import subprocess
import time
from pathlib import Path
from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, List, Optional

import pytest

from playwright._impl._driver import compute_driver_executable
from playwright.async_api import BrowserContext, BrowserType, CDPSession, Page, Worker
from tests.server import Server, TestServerRequest

Expand Down Expand Up @@ -188,11 +190,29 @@ async def test_CR_037_mv3_should_give_access_to_the_service_worker(
launch_persistent_context: LaunchPersistentContext, assetdir: Path
) -> None:
extension_path = assetdir / "extension-mv3-simple"
context = await launch_persistent_context(extension_path)
# Python: ask Node.js for the same host timezone as Intl in the upstream test.
host_timezone = subprocess.check_output(
[
compute_driver_executable()[0],
"-p",
"Intl.DateTimeFormat().resolvedOptions().timeZone",
],
text=True,
).strip()
timezone_id = "America/Jamaica" if host_timezone == "Asia/Tokyo" else "Asia/Tokyo"
context = await launch_persistent_context(extension_path, timezone_id=timezone_id)
service_worker = await get_service_worker(context)
assert service_worker
assert service_worker in context.service_workers
await poll(lambda: service_worker.evaluate("() => globalThis.MAGIC"), 42)
assert await service_worker.evaluate("() => globalThis.initialTimezone") == {
"timezone": timezone_id,
"localDate": (
"2030-01-01T14:00:00.000Z"
if timezone_id == "Asia/Tokyo"
else "2030-01-02T04:00:00.000Z"
),
}
await context.close()
assert len(context.background_pages) == 0

Expand Down
26 changes: 25 additions & 1 deletion tests/async/library/test_browsercontext_network_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import pytest

from playwright.async_api import BrowserContext, Error, Page, Request, Response
from playwright.async_api import BrowserContext, Error, Page, Request, Response, expect
from tests.server import Server, TestServerRequest


Expand Down Expand Up @@ -195,3 +195,27 @@ def handle(request: TestServerRequest) -> None:
with pytest.raises(Error) as exc_info:
await finish_task
assert "closed" in exc_info.value.message


async def test_NE_144_should_not_wait_for_navigation_after_background_cache_revalidation(
context: BrowserContext, server: Server
) -> None:
def handler(req: TestServerRequest) -> None:
req.setHeader("Content-Type", "text/html")
req.setHeader("Cache-Control", "s-maxage=900, stale-while-revalidate=31535100")
req.setHeader("ETag", '"v1"')
if req.getHeader("if-none-match") == '"v1"':
req.setResponseCode(304)
req.finish()
return
req.setResponseCode(200)
req.write(b"<h1>Hello</h1>")
req.finish()

server.set_route("/swr.html", handler)
page1 = await context.new_page()
await page1.goto(server.PREFIX + "/swr.html")
page2 = await context.new_page()
await page2.goto(server.PREFIX + "/swr.html")
await page2.wait_for_timeout(1000)
await expect(page2.locator("h1")).to_have_text("Hello")
23 changes: 23 additions & 0 deletions tests/async/library/test_emulation_focus.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,26 @@ async def test_EM_024_should_trigger_hover_state_concurrently(

await browser1.close()
await browser2.close()


async def test_EM_028_should_keep_mouse_drag_when_interacting_with_another_page_context(
context_factory: Callable[..., Awaitable[BrowserContext]],
) -> None:
page1 = await (await context_factory()).new_page()
page2 = await (await context_factory()).new_page()
for page in [page1, page2]:
await page.set_content(
"""
<body style="margin: 0">
<input type="range" style="margin: 0; width: 200px; height: 20px" onchange="window.changed = true">
</body>
"""
)
await page1.mouse.move(100, 10)
await page1.mouse.down()
await page1.mouse.move(150, 10)
await page2.mouse.move(50, 50)
await page1.mouse.move(180, 10)
await page1.mouse.up()
assert await page1.evaluate("() => !!window['changed']") is True
assert float(await page1.locator("input").input_value()) > 80
Loading
Loading