diff --git a/README.md b/README.md
index 1efcead54..9a5529b13 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
-| Chromium 131.0.6778.33 | ✅ | ✅ | ✅ |
+| Chromium 133.0.6943.16 | ✅ | ✅ | ✅ |
| WebKit 18.2 | ✅ | ✅ | ✅ |
-| Firefox 132.0 | ✅ | ✅ | ✅ |
+| Firefox 134.0 | ✅ | ✅ | ✅ |
## Documentation
diff --git a/playwright/_impl/_assertions.py b/playwright/_impl/_assertions.py
index b226e241f..8ec657531 100644
--- a/playwright/_impl/_assertions.py
+++ b/playwright/_impl/_assertions.py
@@ -525,14 +525,22 @@ async def to_be_checked(
self,
timeout: float = None,
checked: bool = None,
+ indeterminate: bool = None,
) -> None:
__tracebackhide__ = True
- if checked is None:
- checked = True
- checked_string = "checked" if checked else "unchecked"
+ expected_value = {}
+ if indeterminate is not None:
+ expected_value["indeterminate"] = indeterminate
+ if checked is not None:
+ expected_value["checked"] = checked
+ checked_string: str
+ if indeterminate:
+ checked_string = "indeterminate"
+ else:
+ checked_string = "unchecked" if checked is False else "checked"
await self._expect_impl(
- ("to.be.checked" if checked else "to.be.unchecked"),
- FrameExpectOptions(timeout=timeout),
+ "to.be.checked",
+ FrameExpectOptions(timeout=timeout, expectedValue=expected_value),
None,
f"Locator expected to be {checked_string}",
)
@@ -726,7 +734,9 @@ async def to_have_accessible_description(
timeout: float = None,
) -> None:
__tracebackhide__ = True
- expected_values = to_expected_text_values([description], ignoreCase=ignoreCase)
+ expected_values = to_expected_text_values(
+ [description], ignoreCase=ignoreCase, normalize_white_space=True
+ )
await self._expect_impl(
"to.have.accessible.description",
FrameExpectOptions(expectedText=expected_values, timeout=timeout),
@@ -750,7 +760,9 @@ async def to_have_accessible_name(
timeout: float = None,
) -> None:
__tracebackhide__ = True
- expected_values = to_expected_text_values([name], ignoreCase=ignoreCase)
+ expected_values = to_expected_text_values(
+ [name], ignoreCase=ignoreCase, normalize_white_space=True
+ )
await self._expect_impl(
"to.have.accessible.name",
FrameExpectOptions(expectedText=expected_values, timeout=timeout),
@@ -779,6 +791,34 @@ async def to_have_role(self, role: AriaRole, timeout: float = None) -> None:
"Locator expected to have accessible role",
)
+ async def to_have_accessible_error_message(
+ self,
+ errorMessage: Union[str, Pattern[str]],
+ ignoreCase: bool = None,
+ timeout: float = None,
+ ) -> None:
+ __tracebackhide__ = True
+ expected_values = to_expected_text_values(
+ [errorMessage], ignoreCase=ignoreCase, normalize_white_space=True
+ )
+ await self._expect_impl(
+ "to.have.accessible.error.message",
+ FrameExpectOptions(expectedText=expected_values, timeout=timeout),
+ None,
+ "Locator expected to have accessible error message",
+ )
+
+ async def not_to_have_accessible_error_message(
+ self,
+ errorMessage: Union[str, Pattern[str]],
+ ignoreCase: bool = None,
+ timeout: float = None,
+ ) -> None:
+ __tracebackhide__ = True
+ await self._not.to_have_accessible_error_message(
+ errorMessage=errorMessage, ignoreCase=ignoreCase, timeout=timeout
+ )
+
async def not_to_have_role(self, role: AriaRole, timeout: float = None) -> None:
__tracebackhide__ = True
await self._not.to_have_role(role, timeout)
diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py
index 97bb049e3..4b15531af 100644
--- a/playwright/_impl/_network.py
+++ b/playwright/_impl/_network.py
@@ -131,6 +131,7 @@ def __init__(
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
) -> None:
super().__init__(parent, type, guid, initializer)
+ self._channel.mark_as_internal_type()
self._redirected_from: Optional["Request"] = from_nullable_channel(
initializer.get("redirectedFrom")
)
@@ -767,6 +768,7 @@ def __init__(
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
) -> None:
super().__init__(parent, type, guid, initializer)
+ self._channel.mark_as_internal_type()
self._request: Request = from_channel(self._initializer["request"])
timing = self._initializer["timing"]
self._request._timing["startTime"] = timing["startTime"]
diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py
index e1480f5bf..7b92fbafb 100644
--- a/playwright/async_api/_generated.py
+++ b/playwright/async_api/_generated.py
@@ -6879,6 +6879,18 @@ async def pause_at(self, time: typing.Union[float, str, datetime.datetime]) -> N
await page.clock.pause_at(\"2020-02-02\")
```
+ For best results, install the clock before navigating the page and set it to a time slightly before the intended
+ test time. This ensures that all timers run normally during page loading, preventing the page from getting stuck.
+ Once the page has fully loaded, you can safely use `clock.pause_at()` to pause the clock.
+
+ ```py
+ # Initialize clock with some time before the test time and let the page load
+ # naturally. `Date.now` will progress as the timers fire.
+ await page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))
+ await page.goto(\"http://localhost:3333\")
+ await page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))
+ ```
+
Parameters
----------
time : Union[datetime.datetime, float, str]
@@ -8036,7 +8048,7 @@ def set_default_timeout(self, timeout: float) -> None:
Parameters
----------
timeout : float
- Maximum time in milliseconds
+ Maximum time in milliseconds. Pass `0` to disable timeout.
"""
return mapping.from_maybe_impl(
@@ -11497,8 +11509,6 @@ async def pdf(
Returns the PDF buffer.
- **NOTE** Generating a pdf is currently only supported in Chromium headless.
-
`page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call
`page.emulate_media()` before calling `page.pdf()`:
@@ -12750,7 +12760,7 @@ def set_default_timeout(self, timeout: float) -> None:
Parameters
----------
timeout : float
- Maximum time in milliseconds
+ Maximum time in milliseconds. Pass `0` to disable timeout.
"""
return mapping.from_maybe_impl(
@@ -12858,9 +12868,13 @@ async def grant_permissions(
Parameters
----------
permissions : Sequence[str]
- A permission or an array of permissions to grant. Permissions can be one of the following values:
+ A list of permissions to grant.
+
+ **NOTE** Supported permissions differ between browsers, and even between different versions of the same browser.
+ Any permission may stop working after an update.
+
+ Here are some permissions that may be supported by some browsers:
- `'accelerometer'`
- - `'accessibility-events'`
- `'ambient-light-sensor'`
- `'background-sync'`
- `'camera'`
@@ -14161,9 +14175,9 @@ async def close(self, *, reason: typing.Optional[str] = None) -> None:
In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from
the browser server.
- **NOTE** This is similar to force quitting the browser. Therefore, you should call `browser_context.close()`
- on any `BrowserContext`'s you explicitly created earlier with `browser.new_context()` **before** calling
- `browser.close()`.
+ **NOTE** This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close
+ events, call `browser_context.close()` on any `BrowserContext` instances you explicitly created earlier
+ using `browser.new_context()` **before** calling `browser.close()`.
The `Browser` object itself is considered to be disposed and cannot be used anymore.
@@ -14346,7 +14360,7 @@ async def launch(
channel : Union[str, None]
Browser distribution channel.
- Use "chromium" to [opt in to new headless mode](../browsers.md#opt-in-to-new-headless-mode).
+ Use "chromium" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).
Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or
"msedge-canary" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).
@@ -14504,7 +14518,7 @@ async def launch_persistent_context(
channel : Union[str, None]
Browser distribution channel.
- Use "chromium" to [opt in to new headless mode](../browsers.md#opt-in-to-new-headless-mode).
+ Use "chromium" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).
Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or
"msedge-canary" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).
@@ -15522,7 +15536,6 @@ async def dispatch_event(
You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
```py
- # note you can only create data_transfer in chromium and firefox
data_transfer = await page.evaluate_handle(\"new DataTransfer()\")
await locator.dispatch_event(\"#source\", \"dragstart\", {\"dataTransfer\": data_transfer})
```
@@ -16445,18 +16458,22 @@ def or_(self, locator: "Locator") -> "Locator":
Creates a locator matching all elements that match one or both of the two locators.
- Note that when both locators match something, the resulting locator will have multiple matches and violate
- [locator strictness](https://playwright.dev/python/docs/locators#strictness) guidelines.
+ Note that when both locators match something, the resulting locator will have multiple matches, potentially causing
+ a [locator strictness](https://playwright.dev/python/docs/locators#strictness) violation.
**Usage**
Consider a scenario where you'd like to click on a \"New email\" button, but sometimes a security settings dialog
shows up instead. In this case, you can wait for either a \"New email\" button, or a dialog and act accordingly.
+ **NOTE** If both \"New email\" button and security dialog appear on screen, the \"or\" locator will match both of them,
+ possibly throwing the [\"strict mode violation\" error](https://playwright.dev/python/docs/locators#strictness). In this case, you can use
+ `locator.first()` to only match one of them.
+
```py
new_email = page.get_by_role(\"button\", name=\"New\")
dialog = page.get_by_text(\"Confirm security settings\")
- await expect(new_email.or_(dialog)).to_be_visible()
+ await expect(new_email.or_(dialog).first).to_be_visible()
if (await dialog.is_visible()):
await page.get_by_role(\"button\", name=\"Dismiss\").click()
await new_email.click()
@@ -16877,7 +16894,9 @@ async def is_disabled(self, *, timeout: typing.Optional[float] = None) -> bool:
async def is_editable(self, *, timeout: typing.Optional[float] = None) -> bool:
"""Locator.is_editable
- Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).
+ Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable). If the target element is not an ``,
+ `