Merge branch 'microsoft:main' into main
This commit is contained in:
commit
f742e94bb4
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "Playwright",
|
||||
"image": "mcr.microsoft.com/playwright:next",
|
||||
"postCreateCommand": "npm install && npm run build && apt-get update && apt-get install -y software-properties-common && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" && apt-get install -y docker-ce-cli",
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
"runArgs": [
|
||||
"-v", "/var/run/docker.sock:/var/run/docker.sock"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->132.0.6834.6<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->132.0.6834.15<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->132.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
|
|
|
|||
|
|
@ -93,11 +93,20 @@ Element is considered stable when it has maintained the same bounding box for at
|
|||
|
||||
## Enabled
|
||||
|
||||
Element is considered enabled unless it is a `<button>`, `<select>`, `<input>` or `<textarea>` with a `disabled` property.
|
||||
Element is considered enabled when it is **not disabled**.
|
||||
|
||||
Element is **disabled** when:
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` with a `[disabled]` attribute;
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` that is a part of a `<fieldset>` with a `[disabled]` attribute;
|
||||
- it is a descendant of an element with `[aria-disabled=true]` attribute.
|
||||
|
||||
## Editable
|
||||
|
||||
Element is considered editable when it is [enabled] and does not have `readonly` property set.
|
||||
Element is considered editable when it is [enabled] and is **not readonly**.
|
||||
|
||||
Element is **readonly** when:
|
||||
- it is a `<select>`, `<input>` or `<textarea>` with a `[readonly]` attribute;
|
||||
- it has an `[aria-readonly=true]` attribute and an aria role that [supports it](https://w3c.github.io/aria/#aria-readonly).
|
||||
|
||||
## Receives Events
|
||||
|
||||
|
|
|
|||
|
|
@ -1483,7 +1483,7 @@ Boolean disabled = await page.GetByRole(AriaRole.Button).IsDisabledAsync();
|
|||
* since: v1.14
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [editable](../actionability.md#editable).
|
||||
Returns whether the element is [editable](../actionability.md#editable). If the target element is not an `<input>`, `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method throws an error.
|
||||
|
||||
:::warning[Asserting editable state]
|
||||
If you need to assert that an element is editable, prefer [`method: LocatorAssertions.toBeEditable`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
|
||||
|
|
|
|||
|
|
@ -442,6 +442,23 @@ Expected options currently selected.
|
|||
### option: LocatorAssertions.NotToHaveValues.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.23
|
||||
|
||||
## async method: LocatorAssertions.NotToMatchAriaSnapshot
|
||||
* since: v1.49
|
||||
* langs: python
|
||||
|
||||
The opposite of [`method: LocatorAssertions.toMatchAriaSnapshot`].
|
||||
|
||||
### param: LocatorAssertions.NotToMatchAriaSnapshot.expected
|
||||
* since: v1.49
|
||||
- `expected` <string>
|
||||
|
||||
### option: LocatorAssertions.NotToMatchAriaSnapshot.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
### option: LocatorAssertions.NotToMatchAriaSnapshot.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toBeAttached
|
||||
* since: v1.33
|
||||
|
|
@ -2122,7 +2139,7 @@ await expect(page.locator('body')).toMatchAriaSnapshot(`
|
|||
```
|
||||
|
||||
```python async
|
||||
await page.goto('https://demo.playwright.dev/todomvc/')
|
||||
await page.goto("https://demo.playwright.dev/todomvc/")
|
||||
await expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- heading "todos"
|
||||
- textbox "What needs to be done?"
|
||||
|
|
@ -2130,7 +2147,7 @@ await expect(page.locator('body')).to_match_aria_snapshot('''
|
|||
```
|
||||
|
||||
```python sync
|
||||
page.goto('https://demo.playwright.dev/todomvc/')
|
||||
page.goto("https://demo.playwright.dev/todomvc/")
|
||||
expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- heading "todos"
|
||||
- textbox "What needs to be done?"
|
||||
|
|
|
|||
|
|
@ -302,10 +302,10 @@ await test.step('Log in', async () => {
|
|||
```java
|
||||
// All actions between group and groupEnd
|
||||
// will be shown in the trace viewer as a group.
|
||||
page.context().tracing.group("Open Playwright.dev > API");
|
||||
page.context().tracing().group("Open Playwright.dev > API");
|
||||
page.navigate("https://playwright.dev/");
|
||||
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
page.context().tracing.groupEnd();
|
||||
page.context().tracing().groupEnd();
|
||||
```
|
||||
|
||||
```python sync
|
||||
|
|
@ -329,10 +329,10 @@ await page.context.tracing.group_end()
|
|||
```csharp
|
||||
// All actions between GroupAsync and GroupEndAsync
|
||||
// will be shown in the trace viewer as a group.
|
||||
await Page.Context().Tracing.GroupAsync("Open Playwright.dev > API");
|
||||
await Page.Context.Tracing.GroupAsync("Open Playwright.dev > API");
|
||||
await Page.GotoAsync("https://playwright.dev/");
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "API" }).ClickAsync();
|
||||
await Page.Context().Tracing.GroupEndAsync();
|
||||
await Page.Context.Tracing.GroupEndAsync();
|
||||
```
|
||||
|
||||
### param: Tracing.group.name
|
||||
|
|
|
|||
|
|
@ -1,36 +1,142 @@
|
|||
---
|
||||
id: aria-snapshots
|
||||
title: "Aria snapshots"
|
||||
title: "Snapshot testing"
|
||||
---
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Overview
|
||||
|
||||
In Playwright, aria snapshots provide a YAML representation of the accessibility tree of a page.
|
||||
These snapshots can be stored and compared later to verify if the page structure remains consistent or meets defined
|
||||
expectations.
|
||||
With the Playwright Snapshot testing you can assert the accessibility tree of a page against a predefined snapshot template.
|
||||
|
||||
```js
|
||||
await page.goto('https://playwright.dev/');
|
||||
await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
`);
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.goto('https://playwright.dev/')
|
||||
expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.goto('https://playwright.dev/')
|
||||
await expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev/");
|
||||
assertThat(page.locator("banner")).matchesAriaSnapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""");
|
||||
```
|
||||
|
||||
```csharp
|
||||
await page.GotoAsync("https://playwright.dev/");
|
||||
await Expect(page.Locator("banner")).ToMatchAriaSnapshotAsync(@"
|
||||
- banner:
|
||||
- heading ""Playwright enables reliable end-to-end testing for modern web apps."" [level=1]
|
||||
- link ""Get started""
|
||||
- link ""Star microsoft/playwright on GitHub""
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
");
|
||||
```
|
||||
|
||||
<LiteYouTube
|
||||
id="P4R6hnsE0UY"
|
||||
title="Getting started with ARIA Snapshots"
|
||||
/>
|
||||
|
||||
## Assertion testing vs Snapshot testing
|
||||
|
||||
Snapshot testing and assertion testing serve different purposes in test automation:
|
||||
|
||||
### Assertion testing
|
||||
Assertion testing is a targeted approach where you assert specific values or conditions about elements or components. For instance, with Playwright, [`method: LocatorAssertions.toHaveText`]
|
||||
verifies that an element contains the expected text, and [`method: LocatorAssertions.toHaveValue`]
|
||||
confirms that an input field has the expected value.
|
||||
Assertion tests are specific and generally check the current state of an element or property
|
||||
against an expected, predefined state.
|
||||
They work well for predictable, single-value checks but are limited in scope when testing the
|
||||
broader structure or variations.
|
||||
|
||||
**Advantages**
|
||||
- **Clarity**: The intent of the test is explicit and easy to understand.
|
||||
- **Specificity**: Tests focus on particular aspects of functionality, making them more robust
|
||||
against unrelated changes.
|
||||
- **Debugging**: Failures provide targeted feedback, pointing directly to the problematic aspect.
|
||||
|
||||
**Disadvantages**
|
||||
- **Verbose for complex outputs**: Writing assertions for complex data structures or large outputs
|
||||
can be cumbersome and error-prone.
|
||||
- **Maintenance overhead**: As code evolves, manually updating assertions can be time-consuming.
|
||||
|
||||
### Snapshot testing
|
||||
Snapshot testing captures a “snapshot” or representation of the entire
|
||||
state of an element, component, or data at a given moment, which is then saved for future
|
||||
comparisons. When re-running tests, the current state is compared to the snapshot, and if there
|
||||
are differences, the test fails. This approach is especially useful for complex or dynamic
|
||||
structures, where manually asserting each detail would be too time-consuming. Snapshot testing
|
||||
is broader and more holistic than assertion testing, allowing you to track more complex changes over time.
|
||||
|
||||
**Advantages**
|
||||
- **Simplifies complex outputs**: For example, testing a UI component's rendered output can be tedious with traditional assertions. Snapshots capture the entire output for easy comparison.
|
||||
- **Quick Feedback loop**: Developers can easily spot unintended changes in the output.
|
||||
- **Encourages consistency**: Helps maintain consistent output as code evolves.
|
||||
|
||||
**Disadvantages**
|
||||
- **Over-Reliance**: It can be tempting to accept changes to snapshots without fully understanding
|
||||
them, potentially hiding bugs.
|
||||
- **Granularity**: Large snapshots may be hard to interpret when differences arise, especially
|
||||
if minor changes affect large portions of the output.
|
||||
- **Suitability**: Not ideal for highly dynamic content where outputs change frequently or
|
||||
unpredictably.
|
||||
|
||||
### When to use
|
||||
|
||||
- **Snapshot testing** is ideal for:
|
||||
- UI testing of whole pages and components.
|
||||
- Broad structural checks for complex UI components.
|
||||
- Regression testing for outputs that rarely change structure.
|
||||
|
||||
- **Assertion testing** is ideal for:
|
||||
- Core logic validation.
|
||||
- Computed value testing.
|
||||
- Fine-grained tests requiring precise conditions.
|
||||
|
||||
By combining snapshot testing for broad, structural checks and assertion testing for specific functionality, you can achieve a well-rounded testing strategy.
|
||||
|
||||
## Aria snapshots
|
||||
|
||||
In Playwright, aria snapshots provide a YAML representation of the accessibility tree of a page.
|
||||
These snapshots can be stored and compared later to verify if the page structure remains consistent or meets defined
|
||||
expectations.
|
||||
|
||||
The YAML format describes the hierarchical structure of accessible elements on the page, detailing **roles**, **attributes**, **values**, and **text content**.
|
||||
The structure follows a tree-like syntax, where each node represents an accessible element, and indentation indicates
|
||||
nested elements.
|
||||
|
||||
Following is a simple example of an aria snapshot for the playwright.dev homepage:
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
```
|
||||
|
||||
Each accessible element in the tree is represented as a YAML node:
|
||||
|
||||
```yaml
|
||||
|
|
@ -67,19 +173,19 @@ await expect(page.locator('body')).toMatchAriaSnapshot(`
|
|||
```
|
||||
|
||||
```python sync
|
||||
page.locator("body").to_match_aria_snapshot("""
|
||||
expect(page.locator("body")).to_match_aria_snapshot("""
|
||||
- heading "title"
|
||||
""")
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.locator("body").to_match_aria_snapshot("""
|
||||
await expect(page.locator("body")).to_match_aria_snapshot("""
|
||||
- heading "title"
|
||||
""")
|
||||
```
|
||||
|
||||
```java
|
||||
page.locator("body").expect().toMatchAriaSnapshot("""
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
- heading "title"
|
||||
""");
|
||||
```
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ Browser builds for Firefox and WebKit are built for the [glibc](https://en.wikip
|
|||
You can use the [.NET install script](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script) in order to install different SDK versions:
|
||||
|
||||
```bash
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 6.0
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 9.0
|
||||
```
|
||||
|
||||
## Build your own image
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ title: "Installation"
|
|||
|
||||
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||
|
||||
You can choose to use [MSTest base classes](./test-runners.md#mstest) or [NUnit base classes](./test-runners.md#nunit) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
You can choose to use [MSTest base classes](./test-runners.md) or [NUnit base classes](./test-runners.md) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
|
||||
1. Start by creating a new project with `dotnet new`. This will create the `PlaywrightTests` directory which includes a `UnitTest1.cs` file:
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ You can choose any testing framework such as JUnit or TestNG based on your proje
|
|||
|
||||
## .NET
|
||||
|
||||
Playwright for .NET comes with [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners#mstest) and [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners#nunit) for writing end-to-end tests.
|
||||
Playwright for .NET comes with [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners) and [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners) for writing end-to-end tests.
|
||||
|
||||
* [Documentation](https://playwright.dev/dotnet/docs/intro)
|
||||
* [GitHub repo](https://github.com/microsoft/playwright-dotnet)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Getting started - Library"
|
|||
|
||||
## Introduction
|
||||
|
||||
Playwright can either be used with the [MSTest](./test-runners.md#mstest) or [NUnit](./test-runners.md#nunit), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
Playwright can either be used with the [MSTest](./test-runners.md) or [NUnit](./test-runners.md), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,91 @@ toc_max_heading_level: 2
|
|||
---
|
||||
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```csharp
|
||||
await page.GotoAsync("https://playwright.dev");
|
||||
await Expect(page.Locator("body")).ToMatchAriaSnapshotAsync(@"
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link ""Get started""
|
||||
- link ""Star microsoft/playwright on GitHub""
|
||||
- main:
|
||||
- img ""Browsers (Chromium, Firefox, WebKit)""
|
||||
- heading ""Any browser • Any platform • One API""
|
||||
");
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```csharp
|
||||
// All actions between GroupAsync and GroupEndAsync
|
||||
// will be shown in the trace viewer as a group.
|
||||
await Page.Context.Tracing.GroupAsync("Open Playwright.dev > API");
|
||||
await Page.GotoAsync("https://playwright.dev/");
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "API" }).ClickAsync();
|
||||
await Page.Context.Tracing.GroupEndAsync();
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```xml csharp title="runsettings.xml"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Channel>chromium</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Channel=chromium
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
|
|||
|
|
@ -4,6 +4,79 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev");
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
""");
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```java
|
||||
// All actions between group and groupEnd
|
||||
// will be shown in the trace viewer as a group.
|
||||
page.context().tracing().group("Open Playwright.dev > API");
|
||||
page.navigate("https://playwright.dev/");
|
||||
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
page.context().tracing().groupEnd();
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```java
|
||||
Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setChannel("chromium"));
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
|
|||
|
|
@ -4,6 +4,80 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```python
|
||||
page.goto("https://playwright.dev")
|
||||
expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
''')
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```python
|
||||
# All actions between group and group_end
|
||||
# will be shown in the trace viewer as a group.
|
||||
page.context.tracing.group("Open Playwright.dev > API")
|
||||
page.goto("https://playwright.dev/")
|
||||
page.get_by_role("link", name="API").click()
|
||||
page.context.tracing.group_end()
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```bash python
|
||||
pytest test_login.py --browser-channel chromium
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
- Python 3.8 is not supported anymore.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
|
|||
|
|
@ -5,19 +5,57 @@ title: "Test Runners"
|
|||
|
||||
## Introduction
|
||||
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for [MSTest](#mstest) and [NUnit](#nunit). These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for MSTest and NUnit. These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
|
||||
Playwright and Browser instances will be reused between tests for better performance. We
|
||||
recommend running each test case in a new BrowserContext, this way browser state will be
|
||||
isolated between the tests.
|
||||
|
||||
## MSTest
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
Playwright provides base classes to write tests with MSTest via the [`Microsoft.Playwright.MSTest`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest) package.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
### Running MSTest tests in Parallel
|
||||
## Running tests in Parallel
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
Only `ParallelScope.Self` is supported.
|
||||
|
||||
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
||||
|
||||
```bash
|
||||
dotnet test -- NUnit.NumberOfTestWorkers=5
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
By default MSTest will run all classes in parallel, while running tests inside each class sequentially (`ExecutionScope.ClassLevel`). It will create as many processes as there are cores on the host system. You can adjust this behavior by using the following CLI parameter or using a `.runsettings` file, see below.
|
||||
Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is not supported.
|
||||
|
|
@ -26,7 +64,58 @@ Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is
|
|||
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4
|
||||
```
|
||||
|
||||
### Customizing [BrowserContext] options
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [BrowserContext] options
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
|
|
@ -65,7 +154,11 @@ public class ExampleTest : PageTest
|
|||
|
||||
```
|
||||
|
||||
### Customizing [Browser]/launch options
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
|
|
@ -87,14 +180,55 @@ CLI. See the following example:
|
|||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
### Using Verbose API Logs
|
||||
## Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In MSTest, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. Within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
### Using the .runsettings file
|
||||
## Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
For example, to specify the number of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
<!-- For debugging selectors, it's recommend to set the following environment variable -->
|
||||
<DEBUG>pw:api</DEBUG>
|
||||
</EnvironmentVariables>
|
||||
</RunConfiguration>
|
||||
<!-- Playwright -->
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<ExpectTimeout>5000</ExpectTimeout>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
For example, to specify the number of workers, you can use `MSTest.Parallelize.Workers`. You can also enable `DEBUG` logs using `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
|
|
@ -125,131 +259,30 @@ For example, to specify the number of workers, you can use `MSTest.Parallelize.W
|
|||
</RunSettings>
|
||||
```
|
||||
|
||||
### Base MSTest classes for Playwright
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Base classes for Playwright
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.MSTest` namespace:
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|
||||
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
|
||||
|
||||
## NUnit
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
### Running NUnit tests in Parallel
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
Only `ParallelScope.Self` is supported.
|
||||
|
||||
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
||||
|
||||
```bash
|
||||
dotnet test -- NUnit.NumberOfTestWorkers=5
|
||||
```
|
||||
|
||||
### Customizing [BrowserContext] options
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
### Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
### Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
For example, to specify the amount of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
<!-- For debugging selectors, it's recommend to set the following environment variable -->
|
||||
<DEBUG>pw:api</DEBUG>
|
||||
</EnvironmentVariables>
|
||||
</RunConfiguration>
|
||||
<!-- Playwright -->
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<ExpectTimeout>5000</ExpectTimeout>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
### Base NUnit classes for Playwright
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ window.onload = () => {
|
|||
ReactDOM.createRoot(document.querySelector('#root')!).render(<ReportLoader />);
|
||||
};
|
||||
|
||||
const kPlaywrightReportStorageForHMR = 'playwrightReportStorageForHMR';
|
||||
|
||||
class ZipReport implements LoadedReport {
|
||||
private _entries = new Map<string, zip.Entry>();
|
||||
private _json!: HTMLReport;
|
||||
|
|
@ -58,8 +60,20 @@ class ZipReport implements LoadedReport {
|
|||
const zipURI = await new Promise<string>(resolve => {
|
||||
if (window.playwrightReportBase64)
|
||||
return resolve(window.playwrightReportBase64);
|
||||
window.addEventListener('message', event => event.source === window.opener && resolve(event.data), { once: true });
|
||||
if (window.opener) {
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window.opener) {
|
||||
localStorage.setItem(kPlaywrightReportStorageForHMR, event.data);
|
||||
resolve(event.data);
|
||||
}
|
||||
}, { once: true });
|
||||
window.opener.postMessage('ready', '*');
|
||||
} else {
|
||||
const oldReport = localStorage.getItem(kPlaywrightReportStorageForHMR);
|
||||
if (oldReport)
|
||||
return resolve(oldReport);
|
||||
alert('couldnt find report, something with HMR is broken');
|
||||
}
|
||||
});
|
||||
|
||||
const zipReader = new zipjs.ZipReader(new zipjs.Data64URIReader(zipURI), { useWebWorkers: false });
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@
|
|||
"browsers": [
|
||||
{
|
||||
"name": "chromium",
|
||||
"revision": "1149",
|
||||
"revision": "1150",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "132.0.6834.6"
|
||||
"browserVersion": "132.0.6834.15"
|
||||
},
|
||||
{
|
||||
"name": "chromium-headless-shell",
|
||||
"revision": "1149",
|
||||
"revision": "1150",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "132.0.6834.6"
|
||||
"browserVersion": "132.0.6834.15"
|
||||
},
|
||||
{
|
||||
"name": "chromium-tip-of-tree",
|
||||
"revision": "1279",
|
||||
"revision": "1280",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "133.0.6846.0"
|
||||
"browserVersion": "133.0.6850.0"
|
||||
},
|
||||
{
|
||||
"name": "firefox",
|
||||
|
|
@ -33,9 +33,11 @@
|
|||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
"revision": "2105",
|
||||
"revision": "2108",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"debian11-x64": "2105",
|
||||
"debian11-arm64": "2105",
|
||||
"mac10.14": "1446",
|
||||
"mac10.15": "1616",
|
||||
"mac11": "1816",
|
||||
|
|
|
|||
|
|
@ -731,6 +731,10 @@ class FrameSession {
|
|||
if (!frame)
|
||||
return; // Subtree may be already gone due to renderer/browser race.
|
||||
this._page._frameManager.removeChildFramesRecursively(frame);
|
||||
for (const [contextId, context] of this._contextIdToContext) {
|
||||
if (context.frame === frame)
|
||||
this._onExecutionContextDestroyed(contextId);
|
||||
}
|
||||
const frameSession = new FrameSession(this._crPage, session, targetId, this);
|
||||
this._crPage._sessions.set(targetId, frameSession);
|
||||
frameSession._initialize(false).catch(e => e);
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Galaxy S5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 740
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 740,
|
||||
"height": 360
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 320,
|
||||
"height": 658
|
||||
|
|
@ -165,7 +165,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+ landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 658,
|
||||
"height": 320
|
||||
|
|
@ -176,7 +176,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 712,
|
||||
"height": 1138
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1138,
|
||||
"height": 712
|
||||
|
|
@ -1098,7 +1098,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"LG Optimus L70": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -1109,7 +1109,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"LG Optimus L70 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -1120,7 +1120,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 550": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1131,7 +1131,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 550 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1142,7 +1142,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 950": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1153,7 +1153,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 950 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1164,7 +1164,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 800,
|
||||
"height": 1280
|
||||
|
|
@ -1175,7 +1175,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1280,
|
||||
"height": 800
|
||||
|
|
@ -1186,7 +1186,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -1197,7 +1197,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -1208,7 +1208,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1219,7 +1219,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1230,7 +1230,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5X": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1241,7 +1241,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5X landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1252,7 +1252,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1263,7 +1263,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1274,7 +1274,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6P": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1285,7 +1285,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6P landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1296,7 +1296,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 600,
|
||||
"height": 960
|
||||
|
|
@ -1307,7 +1307,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
|
|
@ -1362,7 +1362,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Pixel 2": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 731
|
||||
|
|
@ -1373,7 +1373,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 731,
|
||||
"height": 411
|
||||
|
|
@ -1384,7 +1384,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 XL": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 823
|
||||
|
|
@ -1395,7 +1395,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 XL landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 823,
|
||||
"height": 411
|
||||
|
|
@ -1406,7 +1406,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 393,
|
||||
"height": 786
|
||||
|
|
@ -1417,7 +1417,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 786,
|
||||
"height": 393
|
||||
|
|
@ -1428,7 +1428,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 353,
|
||||
"height": 745
|
||||
|
|
@ -1439,7 +1439,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 745,
|
||||
"height": 353
|
||||
|
|
@ -1450,7 +1450,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G)": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 412,
|
||||
"height": 892
|
||||
|
|
@ -1465,7 +1465,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G) landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"height": 892,
|
||||
"width": 412
|
||||
|
|
@ -1480,7 +1480,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 393,
|
||||
"height": 851
|
||||
|
|
@ -1495,7 +1495,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 851,
|
||||
"height": 393
|
||||
|
|
@ -1510,7 +1510,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 7": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 412,
|
||||
"height": 915
|
||||
|
|
@ -1525,7 +1525,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 7 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 915,
|
||||
"height": 412
|
||||
|
|
@ -1540,7 +1540,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1551,7 +1551,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1562,7 +1562,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Chrome HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1577,7 +1577,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36 Edg/132.0.6834.6",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36 Edg/132.0.6834.15",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1622,7 +1622,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Desktop Chrome": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
@ -1637,7 +1637,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.6 Safari/537.36 Edg/132.0.6834.6",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.15 Safari/537.36 Edg/132.0.6834.15",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
|
|||
|
|
@ -942,7 +942,7 @@ export class Frame extends SdkObject {
|
|||
origin(): string | undefined {
|
||||
if (!this._url.startsWith('http'))
|
||||
return;
|
||||
return network.parsedURL(this._url)?.origin;
|
||||
return network.parseURL(this._url)?.origin;
|
||||
}
|
||||
|
||||
parentFrame(): Frame | null {
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ export class HarTracer {
|
|||
const page = request.frame()?._page;
|
||||
if (this._page && page !== this._page)
|
||||
return;
|
||||
const url = network.parsedURL(request.url());
|
||||
const url = network.parseURL(request.url());
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import type { CSSComplexSelectorList } from '../../utils/isomorphic/cssParser';
|
|||
import { generateSelector, type GenerateSelectorOptions } from './selectorGenerator';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import { Highlight } from './highlight';
|
||||
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription } from './roleUtils';
|
||||
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription, getReadonly } from './roleUtils';
|
||||
import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils';
|
||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
||||
|
|
@ -626,9 +626,12 @@ export class InjectedScript {
|
|||
if (state === 'enabled')
|
||||
return !disabled;
|
||||
|
||||
const editable = !(['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName) && element.hasAttribute('readonly'));
|
||||
if (state === 'editable')
|
||||
return !disabled && editable;
|
||||
if (state === 'editable') {
|
||||
const readonly = getReadonly(element);
|
||||
if (readonly === 'error')
|
||||
throw this.createStacklessError('Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]');
|
||||
return !disabled && !readonly;
|
||||
}
|
||||
|
||||
if (state === 'checked' || state === 'unchecked') {
|
||||
const need = state === 'checked';
|
||||
|
|
|
|||
|
|
@ -860,6 +860,21 @@ export function getChecked(element: Element, allowMixed: boolean): boolean | 'mi
|
|||
return 'error';
|
||||
}
|
||||
|
||||
// https://w3c.github.io/aria/#aria-readonly
|
||||
const kAriaReadonlyRoles = ['checkbox', 'combobox', 'grid', 'gridcell', 'listbox', 'radiogroup', 'slider', 'spinbutton', 'textbox', 'columnheader', 'rowheader', 'searchbox', 'switch', 'treegrid'];
|
||||
export function getReadonly(element: Element): boolean | 'error' {
|
||||
const tagName = elementSafeTagName(element);
|
||||
// https://www.w3.org/TR/wai-aria-1.2/#aria-checked
|
||||
// https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
|
||||
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(tagName))
|
||||
return element.hasAttribute('readonly');
|
||||
if (kAriaReadonlyRoles.includes(getAriaRole(element) || ''))
|
||||
return element.getAttribute('aria-readonly') === 'true';
|
||||
if ((element as HTMLElement).isContentEditable)
|
||||
return false;
|
||||
return 'error';
|
||||
}
|
||||
|
||||
export const kAriaPressedRoles = ['button'];
|
||||
export function getAriaPressed(element: Element): boolean | 'mixed' {
|
||||
// https://www.w3.org/TR/wai-aria-1.2/#aria-pressed
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export function rewriteCookies(cookies: channels.SetNetworkCookie[]): channels.S
|
|||
});
|
||||
}
|
||||
|
||||
export function parsedURL(url: string): URL | null {
|
||||
export function parseURL(url: string): URL | null {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ export const deps: any = {
|
|||
'libgstreamer-gl1.0-0',
|
||||
'libgstreamer-plugins-base1.0-0',
|
||||
'libgstreamer1.0-0',
|
||||
'libgtk-3-0',
|
||||
'libgtk-4-1',
|
||||
'libgudev-1.0-0',
|
||||
'libharfbuzz-icu0',
|
||||
'libharfbuzz0b',
|
||||
|
|
@ -400,6 +400,7 @@ export const deps: any = {
|
|||
'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||
'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||
'libgtk-3.so.0': 'libgtk-3-0',
|
||||
'libgtk-4.so.1': 'libgtk-4-1',
|
||||
'libgudev-1.0.so.0': 'libgudev-1.0-0',
|
||||
'libharfbuzz-icu.so.0': 'libharfbuzz-icu0',
|
||||
'libharfbuzz.so.0': 'libharfbuzz0b',
|
||||
|
|
@ -544,7 +545,7 @@ export const deps: any = {
|
|||
'libgstreamer-plugins-bad1.0-0',
|
||||
'libgstreamer-plugins-base1.0-0',
|
||||
'libgstreamer1.0-0',
|
||||
'libgtk-3-0t64',
|
||||
'libgtk-4-1',
|
||||
'libharfbuzz-icu0',
|
||||
'libharfbuzz0b',
|
||||
'libhyphen0',
|
||||
|
|
@ -621,6 +622,7 @@ export const deps: any = {
|
|||
'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||
'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0',
|
||||
'libgtk-3.so.0': 'libgtk-3-0t64',
|
||||
'libgtk-4.so.1': 'libgtk-4-1',
|
||||
'libharfbuzz-icu.so.0': 'libharfbuzz-icu0',
|
||||
'libharfbuzz.so.0': 'libharfbuzz0b',
|
||||
'libhyphen.so.0': 'libhyphen0',
|
||||
|
|
@ -967,7 +969,7 @@ export const deps: any = {
|
|||
'libgstreamer-gl1.0-0',
|
||||
'libgstreamer-plugins-base1.0-0',
|
||||
'libgstreamer1.0-0',
|
||||
'libgtk-3-0',
|
||||
'libgtk-4-1',
|
||||
'libgudev-1.0-0',
|
||||
'libharfbuzz-icu0',
|
||||
'libharfbuzz0b',
|
||||
|
|
@ -1028,6 +1030,7 @@ export const deps: any = {
|
|||
'libXfixes.so.3': 'libxfixes3',
|
||||
'libxkbcommon.so.0': 'libxkbcommon0',
|
||||
'libXrandr.so.2': 'libxrandr2',
|
||||
'libgtk-4.so.1': 'libgtk-4-1',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
const kCustomElementsAttribute = '__playwright_custom_elements__';
|
||||
const kCurrentSrcAttribute = '__playwright_current_src__';
|
||||
const kBoundingRectAttribute = '__playwright_bounding_rect__';
|
||||
const kPopoverOpenAttribute = '__playwright_popover_open_';
|
||||
|
||||
// Symbols for our own info on Nodes/StyleSheets.
|
||||
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
|
||||
|
|
@ -449,6 +450,12 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
|
|||
expectValue(value);
|
||||
attrs[kBoundingRectAttribute] = value;
|
||||
}
|
||||
if ((element as HTMLElement).popover && (element as HTMLElement).matches && (element as HTMLElement).matches(':popover-open')) {
|
||||
const value = 'true';
|
||||
expectValue(kPopoverOpenAttribute);
|
||||
expectValue(value);
|
||||
attrs[kPopoverOpenAttribute] = value;
|
||||
}
|
||||
if (element.scrollTop) {
|
||||
expectValue(kScrollTopAttribute);
|
||||
expectValue(element.scrollTop);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class WebKit extends BrowserType {
|
|||
override doRewriteStartupLog(error: ProtocolError): ProtocolError {
|
||||
if (!error.logs)
|
||||
return error;
|
||||
if (error.logs.includes('cannot open display'))
|
||||
if (error.logs.includes('Failed to open display') || error.logs.includes('cannot open display'))
|
||||
error.logs = '\n' + wrapInASCIIBox(kNoXServerRunningError, 1);
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,19 +107,15 @@ export function urlMatches(baseURL: string | undefined, urlString: string, match
|
|||
match = globToRegex(match);
|
||||
if (isRegExp(match))
|
||||
return match.test(urlString);
|
||||
if (typeof match === 'string' && match === urlString)
|
||||
return true;
|
||||
const url = parsedURL(urlString);
|
||||
const url = parseURL(urlString);
|
||||
if (!url)
|
||||
return false;
|
||||
if (typeof match === 'string')
|
||||
return url.pathname === match;
|
||||
if (typeof match !== 'function')
|
||||
throw new Error('url parameter should be string, RegExp or function');
|
||||
return match(url);
|
||||
}
|
||||
|
||||
function parsedURL(url: string): URL | null {
|
||||
function parseURL(url: string): URL | null {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (e) {
|
||||
|
|
|
|||
4
packages/playwright-core/types/types.d.ts
vendored
4
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -13680,7 +13680,9 @@ export interface Locator {
|
|||
}): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is [editable](https://playwright.dev/docs/actionability#editable).
|
||||
* Returns whether the element is [editable](https://playwright.dev/docs/actionability#editable). If the target element is not an `<input>`,
|
||||
* `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method
|
||||
* throws an error.
|
||||
*
|
||||
* **NOTE** If you need to assert that an element is editable, prefer
|
||||
* [expect(locator).toBeEditable([options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-editable)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
|
|||
|
||||
const patches: string[] = [];
|
||||
const files: string[] = [];
|
||||
const gitCache = new Map<string, string | null>();
|
||||
|
||||
for (const fileName of [...suggestedRebaselines.keys()].sort()) {
|
||||
const source = await fs.promises.readFile(fileName, 'utf8');
|
||||
|
|
@ -96,10 +97,17 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
|
|||
for (const range of ranges)
|
||||
result = result.substring(0, range.start) + range.newText + result.substring(range.end);
|
||||
|
||||
const relativeName = path.relative(process.cwd(), fileName);
|
||||
if (process.env.PWTEST_UPDATE_SNAPSHOTS === 'overwrite') {
|
||||
await fs.promises.writeFile(fileName, result);
|
||||
} else if (process.env.PWTEST_UPDATE_SNAPSHOTS === 'manual') {
|
||||
await fs.promises.writeFile(fileName, applyPatchWithConflictMarkers(source, result));
|
||||
} else {
|
||||
const gitFolder = findGitRoot(path.dirname(fileName), gitCache);
|
||||
const relativeName = path.relative(gitFolder || process.cwd(), fileName);
|
||||
files.push(relativeName);
|
||||
patches.push(createPatch(relativeName, source, result));
|
||||
}
|
||||
}
|
||||
|
||||
const patchFile = path.join(project.project.outputDir, 'rebaselines.patch');
|
||||
await fs.promises.mkdir(path.dirname(patchFile), { recursive: true });
|
||||
|
|
@ -119,3 +127,62 @@ function createPatch(fileName: string, before: string, after: string) {
|
|||
...text.split('\n').slice(4)
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function findGitRoot(dir: string, cache: Map<string, string | null>): string | null {
|
||||
const result = cache.get(dir);
|
||||
if (result !== undefined)
|
||||
return result;
|
||||
|
||||
const gitPath = path.join(dir, '.git');
|
||||
if (fs.existsSync(gitPath) && fs.lstatSync(gitPath).isDirectory()) {
|
||||
cache.set(dir, dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
const parentDir = path.dirname(dir);
|
||||
if (dir === parentDir) {
|
||||
cache.set(dir, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
const parentResult = findGitRoot(parentDir, cache);
|
||||
cache.set(dir, parentResult);
|
||||
return parentResult;
|
||||
}
|
||||
|
||||
function applyPatchWithConflictMarkers(oldText: string, newText: string) {
|
||||
const diffResult = diff.diffLines(oldText, newText);
|
||||
|
||||
let result = '';
|
||||
let conflict = false;
|
||||
|
||||
diffResult.forEach(part => {
|
||||
if (part.added) {
|
||||
if (conflict) {
|
||||
result += part.value;
|
||||
result += '>>>>>>> SNAPSHOT\n';
|
||||
conflict = false;
|
||||
} else {
|
||||
result += '<<<<<<< HEAD\n';
|
||||
result += part.value;
|
||||
result += '=======\n';
|
||||
conflict = true;
|
||||
}
|
||||
} else if (part.removed) {
|
||||
result += '<<<<<<< HEAD\n';
|
||||
result += part.value;
|
||||
result += '=======\n';
|
||||
conflict = true;
|
||||
} else {
|
||||
if (conflict) {
|
||||
result += '>>>>>>> SNAPSHOT\n';
|
||||
conflict = false;
|
||||
}
|
||||
result += part.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (conflict)
|
||||
result += '>>>>>>> SNAPSHOT\n';
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
2
packages/playwright/types/test.d.ts
vendored
2
packages/playwright/types/test.d.ts
vendored
|
|
@ -1849,7 +1849,7 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
|||
|
||||
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
|
||||
|
||||
type TestDetailsAnnotation = {
|
||||
export type TestDetailsAnnotation = {
|
||||
type: string;
|
||||
description?: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -270,6 +270,13 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
|
|||
(element as HTMLOptionElement).selected = element.getAttribute('__playwright_selected_') === 'true';
|
||||
element.removeAttribute('__playwright_selected_');
|
||||
}
|
||||
for (const element of root.querySelectorAll(`[__playwright_popover_open_]`)) {
|
||||
try {
|
||||
(element as HTMLElement).showPopover();
|
||||
} catch {
|
||||
}
|
||||
element.removeAttribute('__playwright_popover_open_');
|
||||
}
|
||||
|
||||
for (const targetId of targetIds) {
|
||||
for (const target of root.querySelectorAll(`[__playwright_target__="${targetId}"]`)) {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export const androidTest = baseTest.extend<PageTestFixtures & AndroidTestFixture
|
|||
isElectron: [false, { scope: 'worker' }],
|
||||
electronMajorVersion: [0, { scope: 'worker' }],
|
||||
isWebView2: [false, { scope: 'worker' }],
|
||||
isHeadlessShell: [false, { scope: 'worker' }],
|
||||
|
||||
androidDevice: async ({ androidDeviceWorker }, use) => {
|
||||
await closeAllActivities(androidDeviceWorker);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export const electronTest = baseTest.extend<TraceViewerFixtures>(traceViewerFixt
|
|||
isAndroid: [false, { scope: 'worker' }],
|
||||
isElectron: [true, { scope: 'worker' }],
|
||||
isWebView2: [false, { scope: 'worker' }],
|
||||
isHeadlessShell: [false, { scope: 'worker' }],
|
||||
|
||||
launchElectronApp: async ({ playwright }, use) => {
|
||||
// This env prevents 'Electron Security Policy' console message.
|
||||
|
|
|
|||
|
|
@ -101,8 +101,9 @@ it('should change document.activeElement', async ({ page, server }) => {
|
|||
expect(active).toEqual(['INPUT', 'TEXTAREA']);
|
||||
});
|
||||
|
||||
it('should not affect screenshots', async ({ page, server, browserName, headless, isWindows, isHeadlessShell }) => {
|
||||
it('should not affect screenshots', async ({ page, server, browserName, headless, isWindows, isLinux, isHeadlessShell }) => {
|
||||
it.skip(browserName === 'webkit' && isWindows && !headless, 'WebKit/Windows/headed has a larger minimal viewport. See https://github.com/microsoft/playwright/issues/22616');
|
||||
it.skip(browserName === 'webkit' && isLinux && !headless, 'WebKit headed has a larger minimal viewport on gtk4.');
|
||||
it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image');
|
||||
it.fixme(browserName === 'chromium' && !isHeadlessShell, 'https://github.com/microsoft/playwright/issues/33330');
|
||||
|
||||
|
|
|
|||
|
|
@ -1572,3 +1572,20 @@ test('should show only one pointer with multilevel iframes', async ({ page, runA
|
|||
await expect.soft(snapshotFrame.frameLocator('iframe').locator('x-pw-pointer')).not.toBeAttached();
|
||||
await expect.soft(snapshotFrame.frameLocator('iframe').frameLocator('iframe').locator('x-pw-pointer')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show a popover', async ({ runAndTrace, page, server }) => {
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.setContent(`
|
||||
<button popovertarget="pop">Click me</button>
|
||||
<article id="pop" popover="auto">
|
||||
<div>I'm a popover</div>
|
||||
</article>
|
||||
`);
|
||||
await page.getByRole('button').click();
|
||||
await expect(page.locator('div')).toBeVisible();
|
||||
});
|
||||
|
||||
const snapshot = await traceViewer.snapshotFrame('expect.toBeVisible');
|
||||
const popover = snapshot.locator('#pop');
|
||||
await expect.poll(() => popover.evaluate(e => e.matches(':popover-open'))).toBe(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ it.describe('element screenshot', () => {
|
|||
expect(screenshot).toMatchSnapshot('screenshot-element-bounding-box.png');
|
||||
});
|
||||
|
||||
it('should take into account padding and border', async ({ page }) => {
|
||||
it('should take into account padding and border', async ({ page, isLinux, headless, browserName }) => {
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 179 B After Width: | Height: | Size: 332 B |
|
|
@ -138,6 +138,13 @@ test.describe('toBeEditable', () => {
|
|||
const locator = page.locator('input');
|
||||
await expect(locator).not.toBeEditable({ editable: false });
|
||||
});
|
||||
|
||||
test('throws', async ({ page }) => {
|
||||
await page.setContent('<button>');
|
||||
const locator = page.locator('button');
|
||||
const error = await expect(locator).toBeEditable().catch(e => e);
|
||||
expect(error.message).toContain('Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('toBeEnabled', () => {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,15 @@ it('isEnabled and isDisabled should work', async ({ page }) => {
|
|||
});
|
||||
|
||||
it('isEditable should work', async ({ page }) => {
|
||||
await page.setContent(`<input id=input1 disabled><textarea></textarea><input id=input2>`);
|
||||
await page.setContent(`
|
||||
<input id=input1 disabled>
|
||||
<textarea></textarea>
|
||||
<input id=input2>
|
||||
<div contenteditable="true"></div>
|
||||
<span id=span1 role=textbox aria-readonly=true></span>
|
||||
<span id=span2 role=textbox></span>
|
||||
<button>button</button>
|
||||
`);
|
||||
await page.$eval('textarea', t => t.readOnly = true);
|
||||
const input1 = page.locator('#input1');
|
||||
expect(await input1.isEditable()).toBe(false);
|
||||
|
|
@ -130,6 +138,11 @@ it('isEditable should work', async ({ page }) => {
|
|||
const textarea = page.locator('textarea');
|
||||
expect(await textarea.isEditable()).toBe(false);
|
||||
expect(await page.isEditable('textarea')).toBe(false);
|
||||
expect(await page.locator('div').isEditable()).toBe(true);
|
||||
expect(await page.locator('#span1').isEditable()).toBe(false);
|
||||
expect(await page.locator('#span2').isEditable()).toBe(true);
|
||||
const error = await page.locator('button').isEditable().catch(e => e);
|
||||
expect(error.message).toContain('Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]');
|
||||
});
|
||||
|
||||
it('isChecked should work', async ({ page }) => {
|
||||
|
|
|
|||
|
|
@ -238,8 +238,8 @@ it('should fill elements with existing value and selection', async ({ page, serv
|
|||
|
||||
it('should throw nice error without injected script stack when element is not an <input>', async ({ page, server }) => {
|
||||
let error = null;
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.fill('body', '').catch(e => error = e);
|
||||
await page.setContent(`<select><option>value1</option></select>`);
|
||||
await page.fill('select', '').catch(e => error = e);
|
||||
expect(error.message).toContain('page.fill: Error: Element is not an <input>, <textarea> or [contenteditable] element\nCall log:');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -280,12 +280,13 @@ it.describe('page screenshot', () => {
|
|||
expect(screenshot).toMatchSnapshot('screenshot-clip-odd-size.png');
|
||||
});
|
||||
|
||||
it('should work for canvas', async ({ page, server, isElectron, isMac, isLinux, macVersion, browserName, headless }) => {
|
||||
it('should work for canvas', async ({ page, server, isElectron, isMac, isLinux, macVersion, browserName, isHeadlessShell, headless }) => {
|
||||
it.fixme(isElectron && isMac, 'Fails on the bots');
|
||||
it.fixme(browserName === 'webkit' && isLinux && !headless, 'WebKit has slightly different corners on gtk4.');
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.goto(server.PREFIX + '/screenshots/canvas.html');
|
||||
const screenshot = await page.screenshot();
|
||||
if ((!headless && browserName === 'chromium' && isMac && os.arch() === 'arm64' && macVersion >= 14) ||
|
||||
if ((!isHeadlessShell && browserName === 'chromium' && isMac && os.arch() === 'arm64' && macVersion >= 14) ||
|
||||
(browserName === 'webkit' && isLinux && os.arch() === 'x64'))
|
||||
expect(screenshot).toMatchSnapshot('screenshot-canvas-with-accurate-corners.png');
|
||||
else
|
||||
|
|
|
|||
|
|
@ -36,4 +36,5 @@ export type PageWorkerFixtures = {
|
|||
isAndroid: boolean;
|
||||
isElectron: boolean;
|
||||
isWebView2: boolean;
|
||||
isHeadlessShell: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ function trimPatch(patch: string) {
|
|||
|
||||
test('should update snapshot with the update-snapshots flag with multiple projects', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'playwright.config.ts': `
|
||||
export default { projects: [{ name: 'p1' }, { name: 'p2' }] };
|
||||
`,
|
||||
|
|
@ -73,6 +74,7 @@ test('should update snapshot with the update-snapshots flag with multiple projec
|
|||
|
||||
test('should update missing snapshots', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
|
|
@ -116,6 +118,7 @@ test('should update missing snapshots', async ({ runInlineTest }, testInfo) => {
|
|||
|
||||
test('should generate baseline with regex', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
|
|
@ -172,6 +175,7 @@ test('should generate baseline with regex', async ({ runInlineTest }, testInfo)
|
|||
|
||||
test('should generate baseline with special characters', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
|
|
@ -241,6 +245,7 @@ test('should generate baseline with special characters', async ({ runInlineTest
|
|||
|
||||
test('should update missing snapshots in tsx', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'playwright.config.ts': playwrightCtConfigText,
|
||||
'playwright/index.html': `<script type="module" src="./index.ts"></script>`,
|
||||
'playwright/index.ts': ``,
|
||||
|
|
@ -286,6 +291,7 @@ test('should update missing snapshots in tsx', async ({ runInlineTest }, testInf
|
|||
|
||||
test('should update multiple files', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'playwright.config.ts': playwrightCtConfigText,
|
||||
'playwright/index.html': `<script type="module" src="./index.ts"></script>`,
|
||||
'playwright/index.ts': ``,
|
||||
|
|
@ -365,6 +371,7 @@ diff --git a/src/button-2.test.tsx b/src/button-2.test.tsx
|
|||
|
||||
test('should generate baseline for input values', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
|
|
@ -400,6 +407,7 @@ test('should generate baseline for input values', async ({ runInlineTest }, test
|
|||
|
||||
test('should not update snapshots when locator did not match', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'.git/marker': '',
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const webView2Test = baseTest.extend<TraceViewerFixtures>(traceViewerFixt
|
|||
isElectron: [false, { scope: 'worker' }],
|
||||
electronMajorVersion: [0, { scope: 'worker' }],
|
||||
isWebView2: [true, { scope: 'worker' }],
|
||||
isHeadlessShell: [false, { scope: 'worker' }],
|
||||
|
||||
browser: [async ({ playwright }, use, testInfo) => {
|
||||
const cdpPort = 10000 + testInfo.workerIndex;
|
||||
|
|
|
|||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -65,7 +65,7 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
|||
|
||||
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
|
||||
|
||||
type TestDetailsAnnotation = {
|
||||
export type TestDetailsAnnotation = {
|
||||
type: string;
|
||||
description?: string;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue