Merge branch 'main' into sharding-seed
This commit is contained in:
commit
86422bb6d7
|
|
@ -1,6 +1,6 @@
|
||||||
# 🎭 Playwright
|
# 🎭 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://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 -->
|
||||||
|
|
||||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||||
|
|
||||||
|
|
@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
||||||
|
|
||||||
| | Linux | macOS | Windows |
|
| | Linux | macOS | Windows |
|
||||||
| :--- | :---: | :---: | :---: |
|
| :--- | :---: | :---: | :---: |
|
||||||
| Chromium <!-- GEN:chromium-version -->125.0.6422.41<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Chromium <!-- GEN:chromium-version -->126.0.6478.8<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| Firefox <!-- GEN:firefox-version -->125.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Firefox <!-- GEN:firefox-version -->126.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ Playwright performs a range of actionability checks on the elements before makin
|
||||||
behave as expected. It auto-waits for all the relevant checks to pass and only then performs the requested action. If the required checks do not pass within the given `timeout`, action fails with the `TimeoutError`.
|
behave as expected. It auto-waits for all the relevant checks to pass and only then performs the requested action. If the required checks do not pass within the given `timeout`, action fails with the `TimeoutError`.
|
||||||
|
|
||||||
For example, for [`method: Locator.click`], Playwright will ensure that:
|
For example, for [`method: Locator.click`], Playwright will ensure that:
|
||||||
- locator resolves to an exactly one element
|
- locator resolves to exactly one element
|
||||||
- element is [Visible]
|
- element is [Visible]
|
||||||
- element is [Stable], as in not animating or completed animation
|
- element is [Stable], as in not animating or completed animation
|
||||||
- element [Receives Events], as in not obscured by other elements
|
- element [Receives Events], as in not obscured by other elements
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ pnpm create playwright
|
||||||
```
|
```
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -81,10 +82,40 @@ The `tests` folder contains a basic example test to help you get started with te
|
||||||
|
|
||||||
By default tests will be run on all 3 browsers, chromium, firefox and webkit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
|
By default tests will be run on all 3 browsers, chromium, firefox and webkit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="npm"
|
||||||
|
values={[
|
||||||
|
{label: 'npm', value: 'npm'},
|
||||||
|
{label: 'yarn', value: 'yarn'},
|
||||||
|
{label: 'pnpm', value: 'pnpm'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="npm">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test
|
npx playwright test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="yarn">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="pnpm">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm exec playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||

|

|
||||||
See our doc on [Running Tests](./running-tests.md) to learn more about running tests in headed mode, running multiple tests, running specific tests etc.
|
See our doc on [Running Tests](./running-tests.md) to learn more about running tests in headed mode, running multiple tests, running specific tests etc.
|
||||||
|
|
||||||
|
|
@ -92,19 +123,81 @@ See our doc on [Running Tests](./running-tests.md) to learn more about running t
|
||||||
|
|
||||||
After your test completes, an [HTML Reporter](./test-reporters.md#html-reporter) will be generated, which shows you a full report of your tests allowing you to filter the report by browsers, passed tests, failed tests, skipped tests and flaky tests. You can click on each test and explore the test's errors as well as each step of the test. By default, the HTML report is opened automatically if some of the tests failed.
|
After your test completes, an [HTML Reporter](./test-reporters.md#html-reporter) will be generated, which shows you a full report of your tests allowing you to filter the report by browsers, passed tests, failed tests, skipped tests and flaky tests. You can click on each test and explore the test's errors as well as each step of the test. By default, the HTML report is opened automatically if some of the tests failed.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="npm"
|
||||||
|
values={[
|
||||||
|
{label: 'npm', value: 'npm'},
|
||||||
|
{label: 'yarn', value: 'yarn'},
|
||||||
|
{label: 'pnpm', value: 'pnpm'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
<TabItem value="npm">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright show-report
|
npx playwright show-report
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="yarn">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn playwright show-report
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="pnpm">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm exec playwright show-report
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Running the Example Test in UI Mode
|
## Running the Example Test in UI Mode
|
||||||
|
|
||||||
Run your tests with [UI Mode](./test-ui-mode.md) for a better developer experience with time travel debugging, watch mode and more.
|
Run your tests with [UI Mode](./test-ui-mode.md) for a better developer experience with time travel debugging, watch mode and more.
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="npm"
|
||||||
|
values={[
|
||||||
|
{label: 'npm', value: 'npm'},
|
||||||
|
{label: 'yarn', value: 'yarn'},
|
||||||
|
{label: 'pnpm', value: 'pnpm'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
|
||||||
|
<TabItem value="npm">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright test --ui
|
npx playwright test --ui
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="yarn">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn playwright test --ui
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="pnpm">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm exec playwright test --ui
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
||||||
|
|
@ -113,17 +206,84 @@ Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about
|
||||||
|
|
||||||
To update Playwright to the latest version run the following command:
|
To update Playwright to the latest version run the following command:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="npm"
|
||||||
|
values={[
|
||||||
|
{label: 'npm', value: 'npm'},
|
||||||
|
{label: 'yarn', value: 'yarn'},
|
||||||
|
{label: 'pnpm', value: 'pnpm'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
|
||||||
|
<TabItem value="npm">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -D @playwright/test@latest
|
npm install -D @playwright/test@latest
|
||||||
# Also download new browser binaries and their dependencies:
|
# Also download new browser binaries and their dependencies:
|
||||||
npx playwright install --with-deps
|
npx playwright install --with-deps
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="yarn">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add --dev @playwright/test@latest
|
||||||
|
# Also download new browser binaries and their dependencies:
|
||||||
|
yarn playwright install --with-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="pnpm">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install --save-dev @playwright/test@latest
|
||||||
|
# Also download new browser binaries and their dependencies:
|
||||||
|
pnpm exec playwright install --with-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
You can always check which version of Playwright you have by running the following command:
|
You can always check which version of Playwright you have by running the following command:
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
defaultValue="npm"
|
||||||
|
values={[
|
||||||
|
{label: 'npm', value: 'npm'},
|
||||||
|
{label: 'yarn', value: 'yarn'},
|
||||||
|
{label: 'pnpm', value: 'pnpm'}
|
||||||
|
]
|
||||||
|
}>
|
||||||
|
|
||||||
|
<TabItem value="npm">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx playwright --version
|
npx playwright --version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="yarn">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn playwright --version
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem value="pnpm">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm exec playwright --version
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
## System requirements
|
## System requirements
|
||||||
|
|
||||||
- Node.js 18+
|
- Node.js 18+
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,82 @@ title: "Release notes"
|
||||||
toc_max_heading_level: 2
|
toc_max_heading_level: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Version 1.44
|
||||||
|
|
||||||
|
### New APIs
|
||||||
|
|
||||||
|
**Accessibility assertions**
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleName`] checks if the element has the specified accessible name:
|
||||||
|
```csharp
|
||||||
|
var locator = Page.GetByRole(AriaRole.Button);
|
||||||
|
await Expect(locator).ToHaveAccessibleNameAsync("Submit");
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleDescription`] checks if the element has the specified accessible description:
|
||||||
|
```csharp
|
||||||
|
var locator = Page.GetByRole(AriaRole.Button);
|
||||||
|
await Expect(locator).ToHaveAccessibleDescriptionAsync("Upload a photo");
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveRole`] checks if the element has the specified ARIA role:
|
||||||
|
```csharp
|
||||||
|
var locator = Page.GetByTestId("save-button");
|
||||||
|
await Expect(locator).ToHaveRoleAsync(AriaRole.Button);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Locator handler**
|
||||||
|
|
||||||
|
- After executing the handler added with [`method: Page.addLocatorHandler`], Playwright will now wait until the overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with the new `NoWaitAfter` option.
|
||||||
|
- You can use new `Times` option in [`method: Page.addLocatorHandler`] to specify maximum number of times the handler should be run.
|
||||||
|
- The handler in [`method: Page.addLocatorHandler`] now accepts the locator as argument.
|
||||||
|
- New [`method: Page.removeLocatorHandler`] method for removing previously added locator handlers.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var locator = Page.GetByText("This interstitial covers the button");
|
||||||
|
await Page.AddLocatorHandlerAsync(locator, async (overlay) =>
|
||||||
|
{
|
||||||
|
await overlay.Locator("#close").ClickAsync();
|
||||||
|
}, new() { Times = 3, NoWaitAfter = true });
|
||||||
|
// Run your tests that can be interrupted by the overlay.
|
||||||
|
// ...
|
||||||
|
await Page.RemoveLocatorHandlerAsync(locator);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Miscellaneous options**
|
||||||
|
|
||||||
|
- New method [`method: FormData.append`] allows to specify repeating fields with the same name in [`Multipart`](./api/class-apirequestcontext#api-request-context-fetch-option-multipart) option in `APIRequestContext.FetchAsync()`:
|
||||||
|
- ```
|
||||||
|
```csharp
|
||||||
|
var formData = Context.APIRequest.CreateFormData();
|
||||||
|
formData.Append("file", new FilePayload()
|
||||||
|
{
|
||||||
|
Name = "f1.js",
|
||||||
|
MimeType = "text/javascript",
|
||||||
|
Buffer = System.Text.Encoding.UTF8.GetBytes("var x = 2024;")
|
||||||
|
});
|
||||||
|
formData.Append("file", new FilePayload()
|
||||||
|
{
|
||||||
|
Name = "f2.txt",
|
||||||
|
MimeType = "text/plain",
|
||||||
|
Buffer = System.Text.Encoding.UTF8.GetBytes("hello")
|
||||||
|
});
|
||||||
|
var response = await Context.APIRequest.PostAsync("https://example.com/uploadFiles", new() { Multipart = formData });
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: PageAssertions.toHaveURL`] now supports `IgnoreCase` [option](./api/class-pageassertions#page-assertions-to-have-url-option-ignore-case).
|
||||||
|
|
||||||
|
### Browser Versions
|
||||||
|
|
||||||
|
* Chromium 125.0.6422.14
|
||||||
|
* Mozilla Firefox 125.0.1
|
||||||
|
* WebKit 17.4
|
||||||
|
|
||||||
|
This version was also tested against the following stable channels:
|
||||||
|
|
||||||
|
* Google Chrome 124
|
||||||
|
* Microsoft Edge 124
|
||||||
|
|
||||||
## Version 1.43
|
## Version 1.43
|
||||||
|
|
||||||
### New APIs
|
### New APIs
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,72 @@ title: "Release notes"
|
||||||
toc_max_heading_level: 2
|
toc_max_heading_level: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Version 1.44
|
||||||
|
|
||||||
|
### New APIs
|
||||||
|
|
||||||
|
**Accessibility assertions**
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleName`] checks if the element has the specified accessible name:
|
||||||
|
```java
|
||||||
|
Locator locator = page.getByRole(AriaRole.BUTTON);
|
||||||
|
assertThat(locator).hasAccessibleName("Submit");
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleDescription`] checks if the element has the specified accessible description:
|
||||||
|
```java
|
||||||
|
Locator locator = page.getByRole(AriaRole.BUTTON);
|
||||||
|
assertThat(locator).hasAccessibleDescription("Upload a photo");
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveRole`] checks if the element has the specified ARIA role:
|
||||||
|
```java
|
||||||
|
Locator locator = page.getByTestId("save-button");
|
||||||
|
assertThat(locator).hasRole(AriaRole.BUTTON);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Locator handler**
|
||||||
|
|
||||||
|
- After executing the handler added with [`method: Page.addLocatorHandler`], Playwright will now wait until the overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with the new `setNoWaitAfter` option.
|
||||||
|
- You can use new `setTimes` option in [`method: Page.addLocatorHandler`] to specify maximum number of times the handler should be run.
|
||||||
|
- The handler in [`method: Page.addLocatorHandler`] now accepts the locator as argument.
|
||||||
|
- New [`method: Page.removeLocatorHandler`] method for removing previously added locator handlers.
|
||||||
|
|
||||||
|
```java
|
||||||
|
Locator locator = page.getByText("This interstitial covers the button");
|
||||||
|
page.addLocatorHandler(locator, overlay -> {
|
||||||
|
overlay.locator("#close").click();
|
||||||
|
}, new Page.AddLocatorHandlerOptions().setTimes(3).setNoWaitAfter(true));
|
||||||
|
// Run your tests that can be interrupted by the overlay.
|
||||||
|
// ...
|
||||||
|
page.removeLocatorHandler(locator);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Miscellaneous options**
|
||||||
|
|
||||||
|
- New method [`method: FormData.append`] allows to specify repeating fields with the same name in [`setMultipart`](./api/class-requestoptions#request-options-set-multipart) option in `RequestOptions`:
|
||||||
|
```java
|
||||||
|
FormData formData = FormData.create();
|
||||||
|
formData.append("file", new FilePayload("f1.js", "text/javascript",
|
||||||
|
"var x = 2024;".getBytes(StandardCharsets.UTF_8)));
|
||||||
|
formData.append("file", new FilePayload("f2.txt", "text/plain",
|
||||||
|
"hello".getBytes(StandardCharsets.UTF_8)));
|
||||||
|
APIResponse response = context.request().post("https://example.com/uploadFile", RequestOptions.create().setMultipart(formData));
|
||||||
|
```
|
||||||
|
|
||||||
|
- `expect(page).toHaveURL(url)` now supports `setIgnoreCase` [option](./api/class-pageassertions#page-assertions-to-have-url-option-ignore-case).
|
||||||
|
|
||||||
|
### Browser Versions
|
||||||
|
|
||||||
|
* Chromium 125.0.6422.14
|
||||||
|
* Mozilla Firefox 125.0.1
|
||||||
|
* WebKit 17.4
|
||||||
|
|
||||||
|
This version was also tested against the following stable channels:
|
||||||
|
|
||||||
|
* Google Chrome 124
|
||||||
|
* Microsoft Edge 124
|
||||||
|
|
||||||
## Version 1.43
|
## Version 1.43
|
||||||
|
|
||||||
### New APIs
|
### New APIs
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,60 @@ title: "Release notes"
|
||||||
toc_max_heading_level: 2
|
toc_max_heading_level: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Version 1.44
|
||||||
|
|
||||||
|
### New APIs
|
||||||
|
|
||||||
|
**Accessibility assertions**
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleName`] checks if the element has the specified accessible name:
|
||||||
|
```python
|
||||||
|
locator = page.get_by_role("button")
|
||||||
|
expect(locator).to_have_accessible_name("Submit")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveAccessibleDescription`] checks if the element has the specified accessible description:
|
||||||
|
```python
|
||||||
|
locator = page.get_by_role("button")
|
||||||
|
expect(locator).to_have_accessible_description("Upload a photo")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`method: LocatorAssertions.toHaveRole`] checks if the element has the specified ARIA role:
|
||||||
|
```python
|
||||||
|
locator = page.get_by_test_id("save-button")
|
||||||
|
expect(locator).to_have_role("button")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Locator handler**
|
||||||
|
|
||||||
|
- After executing the handler added with [`method: Page.addLocatorHandler`], Playwright will now wait until the overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with the new `no_wait_after` option.
|
||||||
|
- You can use new `times` option in [`method: Page.addLocatorHandler`] to specify maximum number of times the handler should be run.
|
||||||
|
- The handler in [`method: Page.addLocatorHandler`] now accepts the locator as argument.
|
||||||
|
- New [`method: Page.removeLocatorHandler`] method for removing previously added locator handlers.
|
||||||
|
|
||||||
|
```python
|
||||||
|
locator = page.get_by_text("This interstitial covers the button")
|
||||||
|
page.add_locator_handler(locator, lambda overlay: overlay.locator("#close").click(), times=3, no_wait_after=True)
|
||||||
|
# Run your tests that can be interrupted by the overlay.
|
||||||
|
# ...
|
||||||
|
page.remove_locator_handler(locator)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Miscellaneous options**
|
||||||
|
|
||||||
|
- [`method: PageAssertions.toHaveURL`] now supports `ignore_case` [option](./api/class-pageassertions#page-assertions-to-have-url-option-ignore-case).
|
||||||
|
|
||||||
|
### Browser Versions
|
||||||
|
|
||||||
|
* Chromium 125.0.6422.14
|
||||||
|
* Mozilla Firefox 125.0.1
|
||||||
|
* WebKit 17.4
|
||||||
|
|
||||||
|
This version was also tested against the following stable channels:
|
||||||
|
|
||||||
|
* Google Chrome 124
|
||||||
|
* Microsoft Edge 124
|
||||||
|
|
||||||
## Version 1.43
|
## Version 1.43
|
||||||
|
|
||||||
### New APIs
|
### New APIs
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,15 @@ To run a test with a specific title, use the `-g` flag followed by the title of
|
||||||
npx playwright test -g "add a todo item"
|
npx playwright test -g "add a todo item"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Run last failed tests
|
||||||
|
|
||||||
|
To run only the tests that failed in the last test run, first run your tests and then run them again with the `--last-failed` flag.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test --last-failed
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Run tests in VS Code
|
### Run tests in VS Code
|
||||||
|
|
||||||
Tests can be run right from VS Code using the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). Once installed you can simply click the green triangle next to the test you want to run or run all tests from the testing sidebar. Check out our [Getting Started with VS Code](./getting-started-vscode.md#running-tests) guide for more details.
|
Tests can be run right from VS Code using the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). Once installed you can simply click the green triangle next to the test you want to run or run all tests from the testing sidebar. Check out our [Getting Started with VS Code](./getting-started-vscode.md#running-tests) guide for more details.
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,8 @@ import { test, expect } from '@playwright/test';
|
||||||
test('basic test', {
|
test('basic test', {
|
||||||
annotation: {
|
annotation: {
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/microsoft/playwright/issues/23180',
|
description: 'feature tags API',
|
||||||
|
url: 'https://github.com/microsoft/playwright/issues/23180'
|
||||||
},
|
},
|
||||||
}, async ({ page }) => {
|
}, async ({ page }) => {
|
||||||
await page.goto('https://playwright.dev/');
|
await page.goto('https://playwright.dev/');
|
||||||
|
|
@ -97,7 +98,8 @@ Test title.
|
||||||
- `tag` ?<[string]|[Array]<[string]>>
|
- `tag` ?<[string]|[Array]<[string]>>
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]> Annotation type, for example `'issue'`.
|
- `type` <[string]> Annotation type, for example `'issue'`.
|
||||||
- `description` ?<[string]> Optional annotation description, for example an issue url.
|
- `description` ?<[string]> Optional annotation description.
|
||||||
|
- `url` ?<[string]> Optional for example an issue url.
|
||||||
|
|
||||||
Additional test details.
|
Additional test details.
|
||||||
|
|
||||||
|
|
@ -440,6 +442,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
Additional details for all tests in the group.
|
Additional details for all tests in the group.
|
||||||
|
|
||||||
|
|
@ -568,6 +571,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -623,6 +627,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -676,6 +681,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -727,6 +733,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -782,6 +789,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -839,6 +847,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -891,6 +900,7 @@ Group title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.describe`] for details description.
|
See [`method: Test.describe`] for details description.
|
||||||
|
|
||||||
|
|
@ -1109,6 +1119,7 @@ Test title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.(call)`] for test details description.
|
See [`method: Test.(call)`] for test details description.
|
||||||
|
|
||||||
|
|
@ -1214,6 +1225,7 @@ Test title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.(call)`] for test details description.
|
See [`method: Test.(call)`] for test details description.
|
||||||
|
|
||||||
|
|
@ -1291,6 +1303,7 @@ Test title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.(call)`] for test details description.
|
See [`method: Test.(call)`] for test details description.
|
||||||
|
|
||||||
|
|
@ -1436,6 +1449,7 @@ Test title.
|
||||||
- `annotation` ?<[Object]|[Array]<[Object]>>
|
- `annotation` ?<[Object]|[Array]<[Object]>>
|
||||||
- `type` <[string]>
|
- `type` <[string]>
|
||||||
- `description` ?<[string]>
|
- `description` ?<[string]>
|
||||||
|
- `url` ?<[string]>
|
||||||
|
|
||||||
See [`method: Test.(call)`] for test details description.
|
See [`method: Test.(call)`] for test details description.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ Complete set of Playwright Test options is available in the [configuration file]
|
||||||
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from the files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
|
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from the files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
|
||||||
| `-c <file>` or `--config <file>`| Configuration file. If not passed, defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. |
|
| `-c <file>` or `--config <file>`| Configuration file. If not passed, defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. |
|
||||||
| `--debug`| Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options.|
|
| `--debug`| Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options.|
|
||||||
|
| `--fail-on-flaky-tests` | Fails test runs that contain flaky tests. By default flaky tests count as successes. |
|
||||||
| `--forbid-only` | Whether to disallow `test.only`. Useful on CI.|
|
| `--forbid-only` | Whether to disallow `test.only`. Useful on CI.|
|
||||||
| `--global-timeout <number>` | Total timeout for the whole test run in milliseconds. By default, there is no global timeout. Learn more about [various timeouts](./test-timeouts.md).|
|
| `--global-timeout <number>` | Total timeout for the whole test run in milliseconds. By default, there is no global timeout. Learn more about [various timeouts](./test-timeouts.md).|
|
||||||
| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression. For example, this will run `'should add to cart'` when passed `-g "add to cart"`. The regular expression will be tested against the string that consists of the test file name, `test.describe` titles if any, test title and all test tags, separated by spaces, e.g. `my-test.spec.ts my-suite my-test @smoke`. The filter does not apply to the tests from dependency projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies). |
|
| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression. For example, this will run `'should add to cart'` when passed `-g "add to cart"`. The regular expression will be tested against the string that consists of the test file name, `test.describe` titles if any, test title and all test tags, separated by spaces, e.g. `my-test.spec.ts my-suite my-test @smoke`. The filter does not apply to the tests from dependency projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies). |
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,15 @@ export default defineConfig({
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
List report supports the following configuration options and environment variables:
|
||||||
|
|
||||||
|
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||||
|
|---|---|---|---|
|
||||||
|
| `PLAYWRIGHT_LIST_PRINT_STEPS` | `printSteps` | Whether to print each step on its own line. | `false`
|
||||||
|
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
|
||||||
|
|
||||||
### Line reporter
|
### Line reporter
|
||||||
|
|
||||||
Line reporter is more concise than the list reporter. It uses a single line to report last finished test, and prints failures when they occur. Line reporter is useful for large test suites where it shows the progress but does not spam the output by listing all the tests.
|
Line reporter is more concise than the list reporter. It uses a single line to report last finished test, and prints failures when they occur. Line reporter is useful for large test suites where it shows the progress but does not spam the output by listing all the tests.
|
||||||
|
|
@ -127,6 +136,14 @@ Running 124 tests using 6 workers
|
||||||
[23/124] gitignore.spec.ts - should respect nested .gitignore
|
[23/124] gitignore.spec.ts - should respect nested .gitignore
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Line report supports the following configuration options and environment variables:
|
||||||
|
|
||||||
|
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||||
|
|---|---|---|---|
|
||||||
|
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
|
||||||
|
|
||||||
### Dot reporter
|
### Dot reporter
|
||||||
|
|
||||||
Dot reporter is very concise - it only produces a single character per successful test run. It is the default on CI and useful where you don't want a lot of output.
|
Dot reporter is very concise - it only produces a single character per successful test run. It is the default on CI and useful where you don't want a lot of output.
|
||||||
|
|
@ -150,6 +167,24 @@ Running 124 tests using 6 workers
|
||||||
······F·············································
|
······F·············································
|
||||||
```
|
```
|
||||||
|
|
||||||
|
One character is displayed for each test that has run, indicating its status:
|
||||||
|
|
||||||
|
| Character | Description
|
||||||
|
|---|---|
|
||||||
|
| `·` | Passed
|
||||||
|
| `F` | Failed
|
||||||
|
| `×` | Failed or timed out - and will be retried
|
||||||
|
| `±` | Passed on retry (flaky)
|
||||||
|
| `T` | Timed out
|
||||||
|
| `°` | Skipped
|
||||||
|
|
||||||
|
Dot report supports the following configuration options and environment variables:
|
||||||
|
|
||||||
|
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||||
|
|---|---|---|---|
|
||||||
|
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||||
|
|
||||||
### HTML reporter
|
### HTML reporter
|
||||||
|
|
||||||
HTML reporter produces a self-contained folder that contains report for the test run that can be served as a web page.
|
HTML reporter produces a self-contained folder that contains report for the test run that can be served as a web page.
|
||||||
|
|
@ -159,7 +194,7 @@ npx playwright test --reporter=html
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, HTML report is opened automatically if some of the tests failed. You can control this behavior via the
|
By default, HTML report is opened automatically if some of the tests failed. You can control this behavior via the
|
||||||
`open` property in the Playwright config or the `PW_TEST_HTML_REPORT_OPEN` environmental variable. The possible values for that property are `always`, `never` and `on-failure`
|
`open` property in the Playwright config or the `PLAYWRIGHT_HTML_OPEN` environmental variable. The possible values for that property are `always`, `never` and `on-failure`
|
||||||
(default).
|
(default).
|
||||||
|
|
||||||
You can also configure `host` and `port` that are used to serve the HTML report.
|
You can also configure `host` and `port` that are used to serve the HTML report.
|
||||||
|
|
@ -173,7 +208,7 @@ export default defineConfig({
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, report is written into the `playwright-report` folder in the current working directory. One can override
|
By default, report is written into the `playwright-report` folder in the current working directory. One can override
|
||||||
that location using the `PLAYWRIGHT_HTML_REPORT` environment variable or a reporter configuration.
|
that location using the `PLAYWRIGHT_HTML_OUTPUT_DIR` environment variable or a reporter configuration.
|
||||||
|
|
||||||
In configuration file, pass options directly:
|
In configuration file, pass options directly:
|
||||||
|
|
||||||
|
|
@ -207,6 +242,16 @@ Or if there is a custom folder name:
|
||||||
npx playwright show-report my-report
|
npx playwright show-report my-report
|
||||||
```
|
```
|
||||||
|
|
||||||
|
HTML report supports the following configuration options and environment variables:
|
||||||
|
|
||||||
|
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||||
|
|---|---|---|---|
|
||||||
|
| `PLAYWRIGHT_HTML_OUTPUT_DIR` | `outputFolder` | Directory to save the report to. | `playwright-report`
|
||||||
|
| `PLAYWRIGHT_HTML_OPEN` | `open` | When to open the html report in the browser, one of `'always'`, `'never'` or `'on-failure'` | `'on-failure'`
|
||||||
|
| `PLAYWRIGHT_HTML_HOST` | `host` | When report opens in the browser, it will be served bound to this hostname. | `localhost`
|
||||||
|
| `PLAYWRIGHT_HTML_PORT` | `port` | When report opens in the browser, it will be served on this port. | `9323` or any available port when `9323` is not available.
|
||||||
|
| `PLAYWRIGHT_HTML_ATTACHMENTS_BASE_URL` | `attachmentsBaseURL` | A separate location where attachments from the `data` subdirectory are uploaded. Only needed when you upload report and `data` separately to different locations. | `data/`
|
||||||
|
|
||||||
### Blob reporter
|
### Blob reporter
|
||||||
|
|
||||||
Blob reports contain all the details about the test run and can be used later to produce any other report. Their primary function is to facilitate the merging of reports from [sharded tests](./test-sharding.md).
|
Blob reports contain all the details about the test run and can be used later to produce any other report. Their primary function is to facilitate the merging of reports from [sharded tests](./test-sharding.md).
|
||||||
|
|
@ -308,8 +353,8 @@ JUnit report supports following configuration options and environment variables:
|
||||||
| `PLAYWRIGHT_JUNIT_OUTPUT_DIR` | | Directory to save the output file. Ignored if output file is not specified. | `cwd` or config directory.
|
| `PLAYWRIGHT_JUNIT_OUTPUT_DIR` | | Directory to save the output file. Ignored if output file is not specified. | `cwd` or config directory.
|
||||||
| `PLAYWRIGHT_JUNIT_OUTPUT_NAME` | `outputFile` | Base file name for the output, relative to the output dir. | JUnit report is printed to the stdout.
|
| `PLAYWRIGHT_JUNIT_OUTPUT_NAME` | `outputFile` | Base file name for the output, relative to the output dir. | JUnit report is printed to the stdout.
|
||||||
| `PLAYWRIGHT_JUNIT_OUTPUT_FILE` | `outputFile` | Full path to the output file. If defined, `PLAYWRIGHT_JUNIT_OUTPUT_DIR` and `PLAYWRIGHT_JUNIT_OUTPUT_NAME` will be ignored. | JUnit report is printed to the stdout.
|
| `PLAYWRIGHT_JUNIT_OUTPUT_FILE` | `outputFile` | Full path to the output file. If defined, `PLAYWRIGHT_JUNIT_OUTPUT_DIR` and `PLAYWRIGHT_JUNIT_OUTPUT_NAME` will be ignored. | JUnit report is printed to the stdout.
|
||||||
| | `stripANSIControlSequences` | Whether to remove ANSI control sequences from the text before writing it in the report. | By default output text is added as is.
|
| `PLAYWRIGHT_JUNIT_STRIP_ANSI` | `stripANSIControlSequences` | Whether to remove ANSI control sequences from the text before writing it in the report. | By default output text is added as is.
|
||||||
| | `includeProjectInTestName` | Whether to include Playwright project name in every test case as a name prefix. | By default not included.
|
| `PLAYWRIGHT_JUNIT_INCLUDE_PROJECT_IN_TEST_NAME` | `includeProjectInTestName` | Whether to include Playwright project name in every test case as a name prefix. | By default not included.
|
||||||
| `PLAYWRIGHT_JUNIT_SUITE_ID` | | Value of the `id` attribute on the root `<testsuites/>` report entry. | Empty string.
|
| `PLAYWRIGHT_JUNIT_SUITE_ID` | | Value of the `id` attribute on the root `<testsuites/>` report entry. | Empty string.
|
||||||
| `PLAYWRIGHT_JUNIT_SUITE_NAME` | | Value of the `name` attribute on the root `<testsuites/>` report entry. | Empty string.
|
| `PLAYWRIGHT_JUNIT_SUITE_NAME` | | Value of the `name` attribute on the root `<testsuites/>` report entry. | Empty string.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,58 @@ test.describe('New Todo', () => {
|
||||||
await checkNumberOfTodosInLocalStorage(page, 1);
|
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should clear text input field when an item is added 3', async ({ page }) => {
|
||||||
|
// create a new todo locator
|
||||||
|
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||||
|
|
||||||
|
// Create one todo item.
|
||||||
|
await newTodo.fill(TODO_ITEMS[0]);
|
||||||
|
await newTodo.press('Enter');
|
||||||
|
|
||||||
|
// Check that input is empty.
|
||||||
|
await expect(newTodo).toBeEmpty();
|
||||||
|
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clear text input field when an item is added 4', async ({ page }) => {
|
||||||
|
// create a new todo locator
|
||||||
|
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||||
|
|
||||||
|
// Create one todo item.
|
||||||
|
await newTodo.fill(TODO_ITEMS[0]);
|
||||||
|
await newTodo.press('Enter');
|
||||||
|
|
||||||
|
// Check that input is empty.
|
||||||
|
await expect(newTodo).toBeEmpty();
|
||||||
|
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clear text input field when an item is added 5', async ({ page }) => {
|
||||||
|
// create a new todo locator
|
||||||
|
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||||
|
|
||||||
|
// Create one todo item.
|
||||||
|
await newTodo.fill(TODO_ITEMS[0]);
|
||||||
|
await newTodo.press('Enter');
|
||||||
|
|
||||||
|
// Check that input is empty.
|
||||||
|
await expect(newTodo).toBeEmpty();
|
||||||
|
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clear text input field when an item is added 2', async ({ page }) => {
|
||||||
|
// create a new todo locator
|
||||||
|
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||||
|
|
||||||
|
// Create one todo item.
|
||||||
|
await newTodo.fill(TODO_ITEMS[0]);
|
||||||
|
await newTodo.press('Enter');
|
||||||
|
|
||||||
|
// Check that input is empty.
|
||||||
|
await expect(newTodo).toBeEmpty();
|
||||||
|
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||||
|
});
|
||||||
|
|
||||||
test('should append new items to the bottom of the list', async ({ page }) => {
|
test('should append new items to the bottom of the list', async ({ page }) => {
|
||||||
// Create 3 items.
|
// Create 3 items.
|
||||||
await createDefaultTodos(page);
|
await createDefaultTodos(page);
|
||||||
|
|
@ -351,6 +403,22 @@ test.describe('Routing', () => {
|
||||||
await expect(page.getByTestId('todo-item')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
await expect(page.getByTestId('todo-item')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should allow me to display active items 2', async ({ page }) => {
|
||||||
|
await page.locator('.todo-list li .toggle').nth(1).check();
|
||||||
|
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||||
|
await page.getByRole('link', { name: 'Active' }).click();
|
||||||
|
await expect(page.getByTestId('todo-item')).toHaveCount(2);
|
||||||
|
await expect(page.getByTestId('todo-item')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should allow me to display active items 3', async ({ page }) => {
|
||||||
|
await page.locator('.todo-list li .toggle').nth(1).check();
|
||||||
|
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||||
|
await page.getByRole('link', { name: 'Active' }).click();
|
||||||
|
await expect(page.getByTestId('todo-item')).toHaveCount(2);
|
||||||
|
await expect(page.getByTestId('todo-item')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||||
|
});
|
||||||
|
|
||||||
test('should respect the back button', async ({ page }) => {
|
test('should respect the back button', async ({ page }) => {
|
||||||
await page.locator('.todo-list li .toggle').nth(1).check();
|
await page.locator('.todo-list li .toggle').nth(1).check();
|
||||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||||
|
|
@ -429,3 +497,4 @@ async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||||
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
|
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
|
||||||
}, title);
|
}, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ const testCase: TestCase = {
|
||||||
projectName: 'chromium',
|
projectName: 'chromium',
|
||||||
location: { file: 'test.spec.ts', line: 42, column: 0 },
|
location: { file: 'test.spec.ts', line: 42, column: 0 },
|
||||||
annotations: [
|
annotations: [
|
||||||
{ type: 'annotation', description: 'Annotation text' },
|
{ type: 'annotation', description: 'Annotation text', url: 'example url' },
|
||||||
{ type: 'annotation', description: 'Another annotation text' },
|
{ type: 'annotation', description: 'Another annotation text', url: 'Another example url' },
|
||||||
],
|
],
|
||||||
tags: [],
|
tags: [],
|
||||||
outcome: 'expected',
|
outcome: 'expected',
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export type TestFileSummary = {
|
||||||
stats: Stats;
|
stats: Stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TestCaseAnnotation = { type: string, description?: string };
|
export type TestCaseAnnotation = { type: string, description?: string, url?: string};
|
||||||
|
|
||||||
export type TestCaseSummary = {
|
export type TestCaseSummary = {
|
||||||
testId: string,
|
testId: string,
|
||||||
|
|
|
||||||
|
|
@ -3,37 +3,39 @@
|
||||||
"browsers": [
|
"browsers": [
|
||||||
{
|
{
|
||||||
"name": "chromium",
|
"name": "chromium",
|
||||||
"revision": "1118",
|
"revision": "1119",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"browserVersion": "125.0.6422.41"
|
"browserVersion": "126.0.6478.8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chromium-tip-of-tree",
|
"name": "chromium-tip-of-tree",
|
||||||
"revision": "1221",
|
"revision": "1222",
|
||||||
"installByDefault": false,
|
"installByDefault": false,
|
||||||
"browserVersion": "126.0.6478.0"
|
"browserVersion": "126.0.6478.8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1449",
|
"revision": "1450",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"browserVersion": "125.0.1"
|
"browserVersion": "126.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox-beta",
|
"name": "firefox-beta",
|
||||||
"revision": "1449",
|
"revision": "1450",
|
||||||
"installByDefault": false,
|
"installByDefault": false,
|
||||||
"browserVersion": "126.0b1"
|
"browserVersion": "127.0b3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "2007",
|
"revision": "2010",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"revisionOverrides": {
|
"revisionOverrides": {
|
||||||
"mac10.14": "1446",
|
"mac10.14": "1446",
|
||||||
"mac10.15": "1616",
|
"mac10.15": "1616",
|
||||||
"mac11": "1816",
|
"mac11": "1816",
|
||||||
"mac11-arm64": "1816"
|
"mac11-arm64": "1816",
|
||||||
|
"mac12": "2009",
|
||||||
|
"mac12-arm64": "2009"
|
||||||
},
|
},
|
||||||
"browserVersion": "17.4"
|
"browserVersion": "17.4"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -445,6 +445,9 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
return;
|
return;
|
||||||
this._closeReason = options.reason;
|
this._closeReason = options.reason;
|
||||||
this._closeWasCalled = true;
|
this._closeWasCalled = true;
|
||||||
|
await this._wrapApiCall(async () => {
|
||||||
|
await this.request.dispose(options);
|
||||||
|
}, true);
|
||||||
await this._wrapApiCall(async () => {
|
await this._wrapApiCall(async () => {
|
||||||
await this._browserType?._willCloseContext(this);
|
await this._browserType?._willCloseContext(this);
|
||||||
for (const [harId, harParams] of this._harRecorders) {
|
for (const [harId, harParams] of this._harRecorders) {
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,13 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
||||||
async dispose(options: { reason?: string } = {}): Promise<void> {
|
async dispose(options: { reason?: string } = {}): Promise<void> {
|
||||||
this._closeReason = options.reason;
|
this._closeReason = options.reason;
|
||||||
await this._instrumentation.onWillCloseRequestContext(this);
|
await this._instrumentation.onWillCloseRequestContext(this);
|
||||||
await this._channel.dispose(options);
|
try {
|
||||||
|
await this._channel.dispose(options);
|
||||||
|
} catch (e) {
|
||||||
|
if (isTargetClosedError(e))
|
||||||
|
return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
this._tracing._resetStackCounter();
|
this._tracing._resetStackCounter();
|
||||||
this._request?._contexts.delete(this);
|
this._request?._contexts.delete(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
|
||||||
if (this._redirectedFrom)
|
if (this._redirectedFrom)
|
||||||
this._redirectedFrom._redirectedTo = this;
|
this._redirectedFrom._redirectedTo = this;
|
||||||
this._provisionalHeaders = new RawHeaders(initializer.headers);
|
this._provisionalHeaders = new RawHeaders(initializer.headers);
|
||||||
this._fallbackOverrides.postDataBuffer = initializer.postData;
|
|
||||||
this._timing = {
|
this._timing = {
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
domainLookupStart: -1,
|
domainLookupStart: -1,
|
||||||
|
|
@ -128,11 +127,11 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
|
||||||
}
|
}
|
||||||
|
|
||||||
postData(): string | null {
|
postData(): string | null {
|
||||||
return this._fallbackOverrides.postDataBuffer?.toString('utf-8') || null;
|
return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString('utf-8') || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
postDataBuffer(): Buffer | null {
|
postDataBuffer(): Buffer | null {
|
||||||
return this._fallbackOverrides.postDataBuffer || null;
|
return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
postDataJSON(): Object | null {
|
postDataJSON(): Object | null {
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,7 @@ export class CRNetworkManager {
|
||||||
requestPausedSessionInfo!.session._sendMayFail('Fetch.fulfillRequest', {
|
requestPausedSessionInfo!.session._sendMayFail('Fetch.fulfillRequest', {
|
||||||
requestId: requestPausedEvent.requestId,
|
requestId: requestPausedEvent.requestId,
|
||||||
responseCode: 204,
|
responseCode: 204,
|
||||||
responsePhrase: network.STATUS_TEXTS['204'],
|
responsePhrase: network.statusText(204),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
body: '',
|
body: '',
|
||||||
});
|
});
|
||||||
|
|
@ -376,6 +376,10 @@ export class CRNetworkManager {
|
||||||
if (response.body || !expectedLength)
|
if (response.body || !expectedLength)
|
||||||
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||||
|
|
||||||
|
// Make sure no network requests sent while reading the body for fulfilled requests.
|
||||||
|
if (request._route?._fulfilled)
|
||||||
|
return Buffer.from('');
|
||||||
|
|
||||||
// For <link prefetch we are going to receive empty body with non-empty content-length expectation. Reach out for the actual content.
|
// For <link prefetch we are going to receive empty body with non-empty content-length expectation. Reach out for the actual content.
|
||||||
const resource = await session.send('Network.loadNetworkResource', { url: request.request.url(), frameId: this._serviceWorker ? undefined : request.request.frame()!._id, options: { disableCache: false, includeCredentials: true } });
|
const resource = await session.send('Network.loadNetworkResource', { url: request.request.url(), frameId: this._serviceWorker ? undefined : request.request.frame()!._id, options: { disableCache: false, includeCredentials: true } });
|
||||||
const chunks: Buffer[] = [];
|
const chunks: Buffer[] = [];
|
||||||
|
|
@ -595,6 +599,7 @@ class RouteImpl implements network.RouteDelegate {
|
||||||
private readonly _session: CRSession;
|
private readonly _session: CRSession;
|
||||||
private _interceptionId: string;
|
private _interceptionId: string;
|
||||||
_alreadyContinuedParams: Protocol.Fetch.continueRequestParameters | undefined;
|
_alreadyContinuedParams: Protocol.Fetch.continueRequestParameters | undefined;
|
||||||
|
_fulfilled: boolean = false;
|
||||||
|
|
||||||
constructor(session: CRSession, interceptionId: string) {
|
constructor(session: CRSession, interceptionId: string) {
|
||||||
this._session = session;
|
this._session = session;
|
||||||
|
|
@ -615,6 +620,7 @@ class RouteImpl implements network.RouteDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: types.NormalizedFulfillResponse) {
|
async fulfill(response: types.NormalizedFulfillResponse) {
|
||||||
|
this._fulfilled = true;
|
||||||
const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
||||||
|
|
||||||
const responseHeaders = splitSetCookieHeader(response.headers);
|
const responseHeaders = splitSetCookieHeader(response.headers);
|
||||||
|
|
@ -622,7 +628,7 @@ class RouteImpl implements network.RouteDelegate {
|
||||||
await this._session.send('Fetch.fulfillRequest', {
|
await this._session.send('Fetch.fulfillRequest', {
|
||||||
requestId: this._interceptionId!,
|
requestId: this._interceptionId!,
|
||||||
responseCode: response.status,
|
responseCode: response.status,
|
||||||
responsePhrase: network.STATUS_TEXTS[String(response.status)],
|
responsePhrase: network.statusText(response.status),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -841,6 +841,7 @@ CORS RFC1918 enforcement.
|
||||||
clientSecurityState?: Network.ClientSecurityState;
|
clientSecurityState?: Network.ClientSecurityState;
|
||||||
}
|
}
|
||||||
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader";
|
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader";
|
||||||
|
export type SharedDictionaryError = "UseErrorCrossOriginNoCorsRequest"|"UseErrorDictionaryLoadFailure"|"UseErrorMatchingDictionaryNotUsed"|"UseErrorUnexpectedContentDictionaryHeader"|"WriteErrorCossOriginNoCorsRequest"|"WriteErrorDisallowedBySettings"|"WriteErrorExpiredResponse"|"WriteErrorFeatureDisabled"|"WriteErrorInsufficientResources"|"WriteErrorInvalidMatchField"|"WriteErrorInvalidStructuredHeader"|"WriteErrorNavigationRequest"|"WriteErrorNoMatchField"|"WriteErrorNonListMatchDestField"|"WriteErrorNonSecureContext"|"WriteErrorNonStringIdField"|"WriteErrorNonStringInMatchDestList"|"WriteErrorNonStringMatchField"|"WriteErrorNonTokenTypeField"|"WriteErrorRequestAborted"|"WriteErrorShuttingDown"|"WriteErrorTooLongIdField"|"WriteErrorUnsupportedType";
|
||||||
/**
|
/**
|
||||||
* Details for issues around "Attribution Reporting API" usage.
|
* Details for issues around "Attribution Reporting API" usage.
|
||||||
Explainer: https://github.com/WICG/attribution-reporting-api
|
Explainer: https://github.com/WICG/attribution-reporting-api
|
||||||
|
|
@ -870,6 +871,10 @@ instead of "limited-quirks".
|
||||||
url: string;
|
url: string;
|
||||||
location?: SourceCodeLocation;
|
location?: SourceCodeLocation;
|
||||||
}
|
}
|
||||||
|
export interface SharedDictionaryIssueDetails {
|
||||||
|
sharedDictionaryError: SharedDictionaryError;
|
||||||
|
request: AffectedRequest;
|
||||||
|
}
|
||||||
export type GenericIssueErrorType = "CrossOriginPortalPostMessageError"|"FormLabelForNameError"|"FormDuplicateIdForInputError"|"FormInputWithNoLabelError"|"FormAutocompleteAttributeEmptyError"|"FormEmptyIdAndNameAttributesForInputError"|"FormAriaLabelledByToNonExistingId"|"FormInputAssignedAutocompleteValueToIdOrNameAttributeError"|"FormLabelHasNeitherForNorNestedInput"|"FormLabelForMatchesNonExistingIdError"|"FormInputHasWrongButWellIntendedAutocompleteValueError"|"ResponseWasBlockedByORB";
|
export type GenericIssueErrorType = "CrossOriginPortalPostMessageError"|"FormLabelForNameError"|"FormDuplicateIdForInputError"|"FormInputWithNoLabelError"|"FormAutocompleteAttributeEmptyError"|"FormEmptyIdAndNameAttributesForInputError"|"FormAriaLabelledByToNonExistingId"|"FormInputAssignedAutocompleteValueToIdOrNameAttributeError"|"FormLabelHasNeitherForNorNestedInput"|"FormLabelForMatchesNonExistingIdError"|"FormInputHasWrongButWellIntendedAutocompleteValueError"|"ResponseWasBlockedByORB";
|
||||||
/**
|
/**
|
||||||
* Depending on the concrete errorType, different properties are set.
|
* Depending on the concrete errorType, different properties are set.
|
||||||
|
|
@ -997,7 +1002,7 @@ registrations being ignored.
|
||||||
optional fields in InspectorIssueDetails to convey more specific
|
optional fields in InspectorIssueDetails to convey more specific
|
||||||
information about the kind of issue.
|
information about the kind of issue.
|
||||||
*/
|
*/
|
||||||
export type InspectorIssueCode = "CookieIssue"|"MixedContentIssue"|"BlockedByResponseIssue"|"HeavyAdIssue"|"ContentSecurityPolicyIssue"|"SharedArrayBufferIssue"|"LowTextContrastIssue"|"CorsIssue"|"AttributionReportingIssue"|"QuirksModeIssue"|"NavigatorUserAgentIssue"|"GenericIssue"|"DeprecationIssue"|"ClientHintIssue"|"FederatedAuthRequestIssue"|"BounceTrackingIssue"|"CookieDeprecationMetadataIssue"|"StylesheetLoadingIssue"|"FederatedAuthUserInfoRequestIssue"|"PropertyRuleIssue";
|
export type InspectorIssueCode = "CookieIssue"|"MixedContentIssue"|"BlockedByResponseIssue"|"HeavyAdIssue"|"ContentSecurityPolicyIssue"|"SharedArrayBufferIssue"|"LowTextContrastIssue"|"CorsIssue"|"AttributionReportingIssue"|"QuirksModeIssue"|"NavigatorUserAgentIssue"|"GenericIssue"|"DeprecationIssue"|"ClientHintIssue"|"FederatedAuthRequestIssue"|"BounceTrackingIssue"|"CookieDeprecationMetadataIssue"|"StylesheetLoadingIssue"|"FederatedAuthUserInfoRequestIssue"|"PropertyRuleIssue"|"SharedDictionaryIssue";
|
||||||
/**
|
/**
|
||||||
* This struct holds a list of optional fields with additional information
|
* This struct holds a list of optional fields with additional information
|
||||||
specific to the kind of issue. When adding a new issue code, please also
|
specific to the kind of issue. When adding a new issue code, please also
|
||||||
|
|
@ -1024,6 +1029,7 @@ add a new optional field to this type.
|
||||||
stylesheetLoadingIssueDetails?: StylesheetLoadingIssueDetails;
|
stylesheetLoadingIssueDetails?: StylesheetLoadingIssueDetails;
|
||||||
propertyRuleIssueDetails?: PropertyRuleIssueDetails;
|
propertyRuleIssueDetails?: PropertyRuleIssueDetails;
|
||||||
federatedAuthUserInfoRequestIssueDetails?: FederatedAuthUserInfoRequestIssueDetails;
|
federatedAuthUserInfoRequestIssueDetails?: FederatedAuthUserInfoRequestIssueDetails;
|
||||||
|
sharedDictionaryIssueDetails?: SharedDictionaryIssueDetails;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* A unique id for a DevTools inspector issue. Allows other entities (e.g.
|
* A unique id for a DevTools inspector issue. Allows other entities (e.g.
|
||||||
|
|
@ -1121,6 +1127,33 @@ using Audits.issueAdded event.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines commands and events for browser extensions. Available if the client
|
||||||
|
is connected using the --remote-debugging-pipe flag and
|
||||||
|
the --enable-unsafe-extension-debugging flag is set.
|
||||||
|
*/
|
||||||
|
export module Extensions {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs an unpacked extension from the filesystem similar to
|
||||||
|
--load-extension CLI flags. Returns extension ID once the extension
|
||||||
|
has been installed.
|
||||||
|
*/
|
||||||
|
export type loadUnpackedParameters = {
|
||||||
|
/**
|
||||||
|
* Absolute file path.
|
||||||
|
*/
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
export type loadUnpackedReturnValue = {
|
||||||
|
/**
|
||||||
|
* Extension id.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines commands and events for Autofill.
|
* Defines commands and events for Autofill.
|
||||||
*/
|
*/
|
||||||
|
|
@ -3450,7 +3483,7 @@ front-end.
|
||||||
/**
|
/**
|
||||||
* Pseudo element type.
|
* Pseudo element type.
|
||||||
*/
|
*/
|
||||||
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-markers"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
|
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-markers"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
|
||||||
/**
|
/**
|
||||||
* Shadow root type.
|
* Shadow root type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -4426,6 +4459,25 @@ appear on top of all other content.
|
||||||
*/
|
*/
|
||||||
nodeIds: NodeId[];
|
nodeIds: NodeId[];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns the NodeId of the matched element according to certain relations.
|
||||||
|
*/
|
||||||
|
export type getElementByRelationParameters = {
|
||||||
|
/**
|
||||||
|
* Id of the node from which to query the relation.
|
||||||
|
*/
|
||||||
|
nodeId: NodeId;
|
||||||
|
/**
|
||||||
|
* Type of relation to get.
|
||||||
|
*/
|
||||||
|
relation: "PopoverTarget";
|
||||||
|
}
|
||||||
|
export type getElementByRelationReturnValue = {
|
||||||
|
/**
|
||||||
|
* NodeId of the element matching the queried relation.
|
||||||
|
*/
|
||||||
|
nodeId: NodeId;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Re-does the last undone action.
|
* Re-does the last undone action.
|
||||||
*/
|
*/
|
||||||
|
|
@ -8309,8 +8361,16 @@ records.
|
||||||
*/
|
*/
|
||||||
export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler";
|
export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler";
|
||||||
export interface ServiceWorkerRouterInfo {
|
export interface ServiceWorkerRouterInfo {
|
||||||
ruleIdMatched: number;
|
/**
|
||||||
matchedSourceType: ServiceWorkerRouterSource;
|
* ID of the rule matched. If there is a matched rule, this field will
|
||||||
|
be set, otherwiser no value will be set.
|
||||||
|
*/
|
||||||
|
ruleIdMatched?: number;
|
||||||
|
/**
|
||||||
|
* The router source of the matched rule. If there is a matched rule, this
|
||||||
|
field will be set, otherwise no value will be set.
|
||||||
|
*/
|
||||||
|
matchedSourceType?: ServiceWorkerRouterSource;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* HTTP response data.
|
* HTTP response data.
|
||||||
|
|
@ -8385,7 +8445,10 @@ records.
|
||||||
*/
|
*/
|
||||||
fromEarlyHints?: boolean;
|
fromEarlyHints?: boolean;
|
||||||
/**
|
/**
|
||||||
* Information about how Service Worker Static Router was used.
|
* Information about how ServiceWorker Static Router API was used. If this
|
||||||
|
field is set with `matchedSourceType` field, a matching rule is found.
|
||||||
|
If this field is set without `matchedSource`, no matching rule is found.
|
||||||
|
Otherwise, the API is not used.
|
||||||
*/
|
*/
|
||||||
serviceWorkerRouterInfo?: ServiceWorkerRouterInfo;
|
serviceWorkerRouterInfo?: ServiceWorkerRouterInfo;
|
||||||
/**
|
/**
|
||||||
|
|
@ -11167,7 +11230,7 @@ as an ad.
|
||||||
* All Permissions Policy features. This enum should match the one defined
|
* All Permissions Policy features. This enum should match the one defined
|
||||||
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
||||||
*/
|
*/
|
||||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"window-placement"|"xr-spatial-tracking";
|
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
|
||||||
/**
|
/**
|
||||||
* Reason for a permissions policy feature to be disabled.
|
* Reason for a permissions policy feature to be disabled.
|
||||||
*/
|
*/
|
||||||
|
|
@ -11755,7 +11818,7 @@ https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-expl
|
||||||
/**
|
/**
|
||||||
* List of not restored reasons for back-forward cache.
|
* List of not restored reasons for back-forward cache.
|
||||||
*/
|
*/
|
||||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient";
|
||||||
/**
|
/**
|
||||||
* Types of not restored reasons for back-forward cache.
|
* Types of not restored reasons for back-forward cache.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12334,7 +12397,7 @@ option, use with caution.
|
||||||
This API always waits for the manifest to be loaded.
|
This API always waits for the manifest to be loaded.
|
||||||
If manifestId is provided, and it does not match the manifest of the
|
If manifestId is provided, and it does not match the manifest of the
|
||||||
current document, this API errors out.
|
current document, this API errors out.
|
||||||
If there isn’t a loaded page, this API errors out immediately.
|
If there is not a loaded page, this API errors out immediately.
|
||||||
*/
|
*/
|
||||||
export type getAppManifestParameters = {
|
export type getAppManifestParameters = {
|
||||||
manifestId?: string;
|
manifestId?: string;
|
||||||
|
|
@ -16690,7 +16753,7 @@ possible for multiple rule sets and links to trigger a single attempt.
|
||||||
/**
|
/**
|
||||||
* List of FinalStatus reasons for Prerender2.
|
* List of FinalStatus reasons for Prerender2.
|
||||||
*/
|
*/
|
||||||
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl";
|
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled";
|
||||||
/**
|
/**
|
||||||
* Preloading status values, see also PreloadingTriggeringOutcome. This
|
* Preloading status values, see also PreloadingTriggeringOutcome. This
|
||||||
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
|
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
|
||||||
|
|
@ -16921,6 +16984,52 @@ https://web.dev/learn/pwa/web-app-manifest.
|
||||||
badgeCount: number;
|
badgeCount: number;
|
||||||
fileHandlers: FileHandler[];
|
fileHandlers: FileHandler[];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Installs the given manifest identity, optionally using the given install_url
|
||||||
|
or IWA bundle location.
|
||||||
|
|
||||||
|
TODO(crbug.com/337872319) Support IWA to meet the following specific
|
||||||
|
requirement.
|
||||||
|
IWA-specific install description: If the manifest_id is isolated-app://,
|
||||||
|
install_url_or_bundle_url is required, and can be either an http(s) URL or
|
||||||
|
file:// URL pointing to a signed web bundle (.swbn). The .swbn file's
|
||||||
|
signing key must correspond to manifest_id. If Chrome is not in IWA dev
|
||||||
|
mode, the installation will fail, regardless of the state of the allowlist.
|
||||||
|
*/
|
||||||
|
export type installParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
/**
|
||||||
|
* The location of the app or bundle overriding the one derived from the
|
||||||
|
manifestId.
|
||||||
|
*/
|
||||||
|
installUrlOrBundleUrl?: string;
|
||||||
|
}
|
||||||
|
export type installReturnValue = {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Uninstals the given manifest_id and closes any opened app windows.
|
||||||
|
*/
|
||||||
|
export type uninstallParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
}
|
||||||
|
export type uninstallReturnValue = {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Launches the installed web app, or an url in the same web app instead of the
|
||||||
|
default start url if it is provided. Returns a tab / web contents based
|
||||||
|
Target.TargetID which can be used to attach to via Target.attachToTarget or
|
||||||
|
similar APIs.
|
||||||
|
*/
|
||||||
|
export type launchParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
export type launchReturnValue = {
|
||||||
|
/**
|
||||||
|
* ID of the tab target created as a result.
|
||||||
|
*/
|
||||||
|
targetId: Target.TargetID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -19773,6 +19882,7 @@ Error was thrown.
|
||||||
"Audits.enable": Audits.enableParameters;
|
"Audits.enable": Audits.enableParameters;
|
||||||
"Audits.checkContrast": Audits.checkContrastParameters;
|
"Audits.checkContrast": Audits.checkContrastParameters;
|
||||||
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
|
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
|
||||||
|
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters;
|
||||||
"Autofill.trigger": Autofill.triggerParameters;
|
"Autofill.trigger": Autofill.triggerParameters;
|
||||||
"Autofill.setAddresses": Autofill.setAddressesParameters;
|
"Autofill.setAddresses": Autofill.setAddressesParameters;
|
||||||
"Autofill.disable": Autofill.disableParameters;
|
"Autofill.disable": Autofill.disableParameters;
|
||||||
|
|
@ -19870,6 +19980,7 @@ Error was thrown.
|
||||||
"DOM.querySelector": DOM.querySelectorParameters;
|
"DOM.querySelector": DOM.querySelectorParameters;
|
||||||
"DOM.querySelectorAll": DOM.querySelectorAllParameters;
|
"DOM.querySelectorAll": DOM.querySelectorAllParameters;
|
||||||
"DOM.getTopLayerElements": DOM.getTopLayerElementsParameters;
|
"DOM.getTopLayerElements": DOM.getTopLayerElementsParameters;
|
||||||
|
"DOM.getElementByRelation": DOM.getElementByRelationParameters;
|
||||||
"DOM.redo": DOM.redoParameters;
|
"DOM.redo": DOM.redoParameters;
|
||||||
"DOM.removeAttribute": DOM.removeAttributeParameters;
|
"DOM.removeAttribute": DOM.removeAttributeParameters;
|
||||||
"DOM.removeNode": DOM.removeNodeParameters;
|
"DOM.removeNode": DOM.removeNodeParameters;
|
||||||
|
|
@ -20256,6 +20367,9 @@ Error was thrown.
|
||||||
"FedCm.dismissDialog": FedCm.dismissDialogParameters;
|
"FedCm.dismissDialog": FedCm.dismissDialogParameters;
|
||||||
"FedCm.resetCooldown": FedCm.resetCooldownParameters;
|
"FedCm.resetCooldown": FedCm.resetCooldownParameters;
|
||||||
"PWA.getOsAppState": PWA.getOsAppStateParameters;
|
"PWA.getOsAppState": PWA.getOsAppStateParameters;
|
||||||
|
"PWA.install": PWA.installParameters;
|
||||||
|
"PWA.uninstall": PWA.uninstallParameters;
|
||||||
|
"PWA.launch": PWA.launchParameters;
|
||||||
"Console.clearMessages": Console.clearMessagesParameters;
|
"Console.clearMessages": Console.clearMessagesParameters;
|
||||||
"Console.disable": Console.disableParameters;
|
"Console.disable": Console.disableParameters;
|
||||||
"Console.enable": Console.enableParameters;
|
"Console.enable": Console.enableParameters;
|
||||||
|
|
@ -20361,6 +20475,7 @@ Error was thrown.
|
||||||
"Audits.enable": Audits.enableReturnValue;
|
"Audits.enable": Audits.enableReturnValue;
|
||||||
"Audits.checkContrast": Audits.checkContrastReturnValue;
|
"Audits.checkContrast": Audits.checkContrastReturnValue;
|
||||||
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
|
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
|
||||||
|
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue;
|
||||||
"Autofill.trigger": Autofill.triggerReturnValue;
|
"Autofill.trigger": Autofill.triggerReturnValue;
|
||||||
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
|
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
|
||||||
"Autofill.disable": Autofill.disableReturnValue;
|
"Autofill.disable": Autofill.disableReturnValue;
|
||||||
|
|
@ -20458,6 +20573,7 @@ Error was thrown.
|
||||||
"DOM.querySelector": DOM.querySelectorReturnValue;
|
"DOM.querySelector": DOM.querySelectorReturnValue;
|
||||||
"DOM.querySelectorAll": DOM.querySelectorAllReturnValue;
|
"DOM.querySelectorAll": DOM.querySelectorAllReturnValue;
|
||||||
"DOM.getTopLayerElements": DOM.getTopLayerElementsReturnValue;
|
"DOM.getTopLayerElements": DOM.getTopLayerElementsReturnValue;
|
||||||
|
"DOM.getElementByRelation": DOM.getElementByRelationReturnValue;
|
||||||
"DOM.redo": DOM.redoReturnValue;
|
"DOM.redo": DOM.redoReturnValue;
|
||||||
"DOM.removeAttribute": DOM.removeAttributeReturnValue;
|
"DOM.removeAttribute": DOM.removeAttributeReturnValue;
|
||||||
"DOM.removeNode": DOM.removeNodeReturnValue;
|
"DOM.removeNode": DOM.removeNodeReturnValue;
|
||||||
|
|
@ -20844,6 +20960,9 @@ Error was thrown.
|
||||||
"FedCm.dismissDialog": FedCm.dismissDialogReturnValue;
|
"FedCm.dismissDialog": FedCm.dismissDialogReturnValue;
|
||||||
"FedCm.resetCooldown": FedCm.resetCooldownReturnValue;
|
"FedCm.resetCooldown": FedCm.resetCooldownReturnValue;
|
||||||
"PWA.getOsAppState": PWA.getOsAppStateReturnValue;
|
"PWA.getOsAppState": PWA.getOsAppStateReturnValue;
|
||||||
|
"PWA.install": PWA.installReturnValue;
|
||||||
|
"PWA.uninstall": PWA.uninstallReturnValue;
|
||||||
|
"PWA.launch": PWA.launchReturnValue;
|
||||||
"Console.clearMessages": Console.clearMessagesReturnValue;
|
"Console.clearMessages": Console.clearMessagesReturnValue;
|
||||||
"Console.disable": Console.disableReturnValue;
|
"Console.disable": Console.disableReturnValue;
|
||||||
"Console.enable": Console.enableReturnValue;
|
"Console.enable": Console.enableReturnValue;
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy S5": {
|
"Galaxy S5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -121,7 +121,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S5 landscape": {
|
"Galaxy S5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S8": {
|
"Galaxy S8": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 740
|
"height": 740
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S8 landscape": {
|
"Galaxy S8 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 740,
|
"width": 740,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -154,7 +154,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S9+": {
|
"Galaxy S9+": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 658
|
"height": 658
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S9+ landscape": {
|
"Galaxy S9+ landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 658,
|
"width": 658,
|
||||||
"height": 320
|
"height": 320
|
||||||
|
|
@ -176,7 +176,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy Tab S4": {
|
"Galaxy Tab S4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 712,
|
"width": 712,
|
||||||
"height": 1138
|
"height": 1138
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy Tab S4 landscape": {
|
"Galaxy Tab S4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1138,
|
"width": 1138,
|
||||||
"height": 712
|
"height": 712
|
||||||
|
|
@ -978,7 +978,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"LG Optimus L70": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 384,
|
"width": 384,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -989,7 +989,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"LG Optimus L70 landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 384
|
"height": 384
|
||||||
|
|
@ -1000,7 +1000,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 550": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -1011,7 +1011,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 550 landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -1022,7 +1022,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 950": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -1033,7 +1033,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 950 landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -1044,7 +1044,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 10": {
|
"Nexus 10": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 800,
|
"width": 800,
|
||||||
"height": 1280
|
"height": 1280
|
||||||
|
|
@ -1055,7 +1055,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 10 landscape": {
|
"Nexus 10 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1280,
|
"width": 1280,
|
||||||
"height": 800
|
"height": 800
|
||||||
|
|
@ -1066,7 +1066,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 4": {
|
"Nexus 4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 384,
|
"width": 384,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -1077,7 +1077,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 4 landscape": {
|
"Nexus 4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 384
|
"height": 384
|
||||||
|
|
@ -1088,7 +1088,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5": {
|
"Nexus 5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -1099,7 +1099,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5 landscape": {
|
"Nexus 5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -1110,7 +1110,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5X": {
|
"Nexus 5X": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
|
@ -1121,7 +1121,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5X landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
|
@ -1132,7 +1132,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6": {
|
"Nexus 6": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
|
@ -1143,7 +1143,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6 landscape": {
|
"Nexus 6 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
|
@ -1154,7 +1154,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6P": {
|
"Nexus 6P": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
|
@ -1165,7 +1165,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6P landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
|
@ -1176,7 +1176,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 7": {
|
"Nexus 7": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 960
|
"height": 960
|
||||||
|
|
@ -1187,7 +1187,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 7 landscape": {
|
"Nexus 7 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 960,
|
"width": 960,
|
||||||
"height": 600
|
"height": 600
|
||||||
|
|
@ -1242,7 +1242,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Pixel 2": {
|
"Pixel 2": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 411,
|
"width": 411,
|
||||||
"height": 731
|
"height": 731
|
||||||
|
|
@ -1253,7 +1253,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 landscape": {
|
"Pixel 2 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 731,
|
"width": 731,
|
||||||
"height": 411
|
"height": 411
|
||||||
|
|
@ -1264,7 +1264,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 XL": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 411,
|
"width": 411,
|
||||||
"height": 823
|
"height": 823
|
||||||
|
|
@ -1275,7 +1275,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 XL landscape": {
|
"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/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 823,
|
"width": 823,
|
||||||
"height": 411
|
"height": 411
|
||||||
|
|
@ -1286,7 +1286,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 3": {
|
"Pixel 3": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 393,
|
"width": 393,
|
||||||
"height": 786
|
"height": 786
|
||||||
|
|
@ -1297,7 +1297,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 3 landscape": {
|
"Pixel 3 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 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/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 786,
|
"width": 786,
|
||||||
"height": 393
|
"height": 393
|
||||||
|
|
@ -1308,7 +1308,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4": {
|
"Pixel 4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 353,
|
"width": 353,
|
||||||
"height": 745
|
"height": 745
|
||||||
|
|
@ -1319,7 +1319,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4 landscape": {
|
"Pixel 4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 745,
|
"width": 745,
|
||||||
"height": 353
|
"height": 353
|
||||||
|
|
@ -1330,7 +1330,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4a (5G)": {
|
"Pixel 4a (5G)": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 892
|
"height": 892
|
||||||
|
|
@ -1345,7 +1345,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4a (5G) landscape": {
|
"Pixel 4a (5G) landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"height": 892,
|
"height": 892,
|
||||||
"width": 412
|
"width": 412
|
||||||
|
|
@ -1360,7 +1360,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 5": {
|
"Pixel 5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 393,
|
"width": 393,
|
||||||
"height": 851
|
"height": 851
|
||||||
|
|
@ -1375,7 +1375,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 5 landscape": {
|
"Pixel 5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 851,
|
"width": 851,
|
||||||
"height": 393
|
"height": 393
|
||||||
|
|
@ -1390,7 +1390,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 7": {
|
"Pixel 7": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 915
|
"height": 915
|
||||||
|
|
@ -1405,7 +1405,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 7 landscape": {
|
"Pixel 7 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 915,
|
"width": 915,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
|
@ -1420,7 +1420,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Moto G4": {
|
"Moto G4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
|
@ -1431,7 +1431,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Moto G4 landscape": {
|
"Moto G4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
|
@ -1442,7 +1442,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Chrome HiDPI": {
|
"Desktop Chrome HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
|
@ -1457,7 +1457,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Edge HiDPI": {
|
"Desktop Edge HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36 Edg/125.0.6422.41",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36 Edg/126.0.6478.8",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
|
@ -1472,7 +1472,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox HiDPI": {
|
"Desktop Firefox HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0.1) Gecko/20100101 Firefox/125.0.1",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
|
@ -1502,7 +1502,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Desktop Chrome": {
|
"Desktop Chrome": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
@ -1517,7 +1517,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Edge": {
|
"Desktop Edge": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.41 Safari/537.36 Edg/125.0.6422.41",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.8 Safari/537.36 Edg/126.0.6478.8",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
@ -1532,7 +1532,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox": {
|
"Desktop Firefox": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0.1) Gecko/20100101 Firefox/125.0.1",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ export class Electron extends SdkObject {
|
||||||
return controller.run(async progress => {
|
return controller.run(async progress => {
|
||||||
let app: ElectronApplication | undefined = undefined;
|
let app: ElectronApplication | undefined = undefined;
|
||||||
// --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it.
|
// --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it.
|
||||||
const electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...args];
|
let electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...args];
|
||||||
|
|
||||||
if (os.platform() === 'linux') {
|
if (os.platform() === 'linux') {
|
||||||
const runningAsRoot = process.geteuid && process.geteuid() === 0;
|
const runningAsRoot = process.geteuid && process.geteuid() === 0;
|
||||||
|
|
@ -195,6 +195,16 @@ export class Electron extends SdkObject {
|
||||||
// Packaged apps might have their own command line handling.
|
// Packaged apps might have their own command line handling.
|
||||||
electronArguments.unshift('-r', require.resolve('./loader'));
|
electronArguments.unshift('-r', require.resolve('./loader'));
|
||||||
}
|
}
|
||||||
|
let shell = false;
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// On Windows in order to run .cmd files, shell: true is required.
|
||||||
|
// https://github.com/nodejs/node/issues/52554
|
||||||
|
shell = true;
|
||||||
|
// On Windows, we need to quote the executable path due to shell: true.
|
||||||
|
command = `"${command}"`;
|
||||||
|
// On Windows, we need to quote the arguments due to shell: true.
|
||||||
|
electronArguments = electronArguments.map(arg => `"${arg}"`);
|
||||||
|
}
|
||||||
|
|
||||||
// When debugging Playwright test that runs Electron, NODE_OPTIONS
|
// When debugging Playwright test that runs Electron, NODE_OPTIONS
|
||||||
// will make the debugger attach to Electron's Node. But Playwright
|
// will make the debugger attach to Electron's Node. But Playwright
|
||||||
|
|
@ -208,9 +218,7 @@ export class Electron extends SdkObject {
|
||||||
progress.log(message);
|
progress.log(message);
|
||||||
browserLogsCollector.log(message);
|
browserLogsCollector.log(message);
|
||||||
},
|
},
|
||||||
// On Windows in order to run .cmd files, shell: true is required.
|
shell,
|
||||||
// https://github.com/nodejs/node/issues/52554
|
|
||||||
shell: process.platform === 'win32',
|
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
cwd: options.cwd,
|
cwd: options.cwd,
|
||||||
tempDirectories: [artifactsDir],
|
tempDirectories: [artifactsDir],
|
||||||
|
|
@ -221,6 +229,8 @@ export class Electron extends SdkObject {
|
||||||
onExit: () => app?.emit(ElectronApplication.Events.Close),
|
onExit: () => app?.emit(ElectronApplication.Events.Close),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// All waitForLines must be started immediately.
|
||||||
|
// Otherwise the lines might come before we are ready.
|
||||||
const waitForXserverError = new Promise(async (resolve, reject) => {
|
const waitForXserverError = new Promise(async (resolve, reject) => {
|
||||||
waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => reject(new Error([
|
waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => reject(new Error([
|
||||||
'Unable to open X display!',
|
'Unable to open X display!',
|
||||||
|
|
@ -232,17 +242,20 @@ export class Electron extends SdkObject {
|
||||||
progress.metadata.log
|
progress.metadata.log
|
||||||
].join('\n')))).catch(() => {});
|
].join('\n')))).catch(() => {});
|
||||||
});
|
});
|
||||||
|
const nodeMatchPromise = waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
|
||||||
|
const chromeMatchPromise = waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
|
||||||
|
const debuggerDisconnectPromise = waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
|
||||||
|
|
||||||
const nodeMatch = await waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
|
const nodeMatch = await nodeMatchPromise;
|
||||||
const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]);
|
const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]);
|
||||||
const nodeConnection = new CRConnection(nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
|
const nodeConnection = new CRConnection(nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
|
||||||
|
|
||||||
// Immediately release exiting process under debug.
|
// Immediately release exiting process under debug.
|
||||||
waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./).then(() => {
|
debuggerDisconnectPromise.then(() => {
|
||||||
nodeTransport.close();
|
nodeTransport.close();
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
const chromeMatch = await Promise.race([
|
const chromeMatch = await Promise.race([
|
||||||
waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/),
|
chromeMatchPromise,
|
||||||
waitForXserverError,
|
waitForXserverError,
|
||||||
]) as RegExpMatchArray;
|
]) as RegExpMatchArray;
|
||||||
const chromeTransport = await WebSocketTransport.connect(progress, chromeMatch[1]);
|
const chromeTransport = await WebSocketTransport.connect(progress, chromeMatch[1]);
|
||||||
|
|
|
||||||
|
|
@ -336,12 +336,15 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
redirectOptions.rejectUnauthorized = false;
|
redirectOptions.rejectUnauthorized = false;
|
||||||
|
|
||||||
// HTTP-redirect fetch step 4: If locationURL is null, then return response.
|
// HTTP-redirect fetch step 4: If locationURL is null, then return response.
|
||||||
if (response.headers.location) {
|
// Best-effort UTF-8 decoding, per spec it's US-ASCII only, but browsers are more lenient.
|
||||||
|
// Node.js parses it as Latin1 via std::v8::String, so we convert it to UTF-8.
|
||||||
|
const locationHeaderValue = Buffer.from(response.headers.location ?? '', 'latin1').toString('utf8');
|
||||||
|
if (locationHeaderValue) {
|
||||||
let locationURL;
|
let locationURL;
|
||||||
try {
|
try {
|
||||||
locationURL = new URL(response.headers.location, url);
|
locationURL = new URL(locationHeaderValue, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(new Error(`uri requested responds with an invalid redirect URL: ${response.headers.location}`));
|
reject(new Error(`uri requested responds with an invalid redirect URL: ${locationHeaderValue}`));
|
||||||
request.destroy();
|
request.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ class FFRouteImpl implements network.RouteDelegate {
|
||||||
await this._session.sendMayFail('Network.fulfillInterceptedRequest', {
|
await this._session.sendMayFail('Network.fulfillInterceptedRequest', {
|
||||||
requestId: this._request._id,
|
requestId: this._request._id,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: network.STATUS_TEXTS[String(response.status)] || '',
|
statusText: network.statusText(response.status),
|
||||||
headers: response.headers,
|
headers: response.headers,
|
||||||
base64body,
|
base64body,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -616,7 +616,7 @@ export interface RouteDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
||||||
export const STATUS_TEXTS: { [status: string]: string } = {
|
const STATUS_TEXTS: { [status: string]: string } = {
|
||||||
'100': 'Continue',
|
'100': 'Continue',
|
||||||
'101': 'Switching Protocols',
|
'101': 'Switching Protocols',
|
||||||
'102': 'Processing',
|
'102': 'Processing',
|
||||||
|
|
@ -682,6 +682,10 @@ export const STATUS_TEXTS: { [status: string]: string } = {
|
||||||
'511': 'Network Authentication Required',
|
'511': 'Network Authentication Required',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function statusText(status: number): string {
|
||||||
|
return STATUS_TEXTS[String(status)] || 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
export function singleHeader(name: string, value: string): HeadersArray {
|
export function singleHeader(name: string, value: string): HeadersArray {
|
||||||
return [{ name, value }];
|
return [{ name, value }];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,17 +137,17 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = {
|
||||||
'debian11-arm64': 'builds/firefox/%s/firefox-debian-11-arm64.zip',
|
'debian11-arm64': 'builds/firefox/%s/firefox-debian-11-arm64.zip',
|
||||||
'debian12-x64': 'builds/firefox/%s/firefox-debian-12.zip',
|
'debian12-x64': 'builds/firefox/%s/firefox-debian-12.zip',
|
||||||
'debian12-arm64': 'builds/firefox/%s/firefox-debian-12-arm64.zip',
|
'debian12-arm64': 'builds/firefox/%s/firefox-debian-12-arm64.zip',
|
||||||
'mac10.13': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac10.13': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac10.14': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac10.14': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac10.15': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac10.15': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac11': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac11': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac11-arm64': 'builds/firefox/%s/firefox-mac-13-arm64.zip',
|
'mac11-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
|
||||||
'mac12': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac12': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac12-arm64': 'builds/firefox/%s/firefox-mac-13-arm64.zip',
|
'mac12-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
|
||||||
'mac13': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac13': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac13-arm64': 'builds/firefox/%s/firefox-mac-13-arm64.zip',
|
'mac13-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
|
||||||
'mac14': 'builds/firefox/%s/firefox-mac-13.zip',
|
'mac14': 'builds/firefox/%s/firefox-mac.zip',
|
||||||
'mac14-arm64': 'builds/firefox/%s/firefox-mac-13-arm64.zip',
|
'mac14-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
|
||||||
'win64': 'builds/firefox/%s/firefox-win64.zip',
|
'win64': 'builds/firefox/%s/firefox-win64.zip',
|
||||||
},
|
},
|
||||||
'firefox-beta': {
|
'firefox-beta': {
|
||||||
|
|
@ -162,17 +162,17 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = {
|
||||||
'debian11-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip',
|
'debian11-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip',
|
||||||
'debian12-x64': 'builds/firefox-beta/%s/firefox-beta-debian-12.zip',
|
'debian12-x64': 'builds/firefox-beta/%s/firefox-beta-debian-12.zip',
|
||||||
'debian12-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip',
|
'debian12-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip',
|
||||||
'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac11': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac11': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac11-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-13-arm64.zip',
|
'mac11-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
|
||||||
'mac12': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac12': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac12-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-13-arm64.zip',
|
'mac12-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
|
||||||
'mac13': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac13': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac13-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-13-arm64.zip',
|
'mac13-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
|
||||||
'mac14': 'builds/firefox-beta/%s/firefox-beta-mac-13.zip',
|
'mac14': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
|
||||||
'mac14-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-13-arm64.zip',
|
'mac14-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
|
||||||
'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip',
|
'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip',
|
||||||
},
|
},
|
||||||
'webkit': {
|
'webkit': {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ export type TraceViewerRedirectOptions = {
|
||||||
reporter?: string[];
|
reporter?: string[];
|
||||||
webApp?: string;
|
webApp?: string;
|
||||||
isServer?: boolean;
|
isServer?: boolean;
|
||||||
|
outputDir?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TraceViewerAppOptions = {
|
export type TraceViewerAppOptions = {
|
||||||
|
|
@ -129,10 +130,12 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[
|
||||||
params.append('timeout', String(options.timeout));
|
params.append('timeout', String(options.timeout));
|
||||||
if (options.headed)
|
if (options.headed)
|
||||||
params.append('headed', '');
|
params.append('headed', '');
|
||||||
|
if (options.outputDir)
|
||||||
|
params.append('outputDir', options.outputDir);
|
||||||
for (const reporter of options.reporter || [])
|
for (const reporter of options.reporter || [])
|
||||||
params.append('reporter', reporter);
|
params.append('reporter', reporter);
|
||||||
|
|
||||||
const urlPath = `/trace/${options.webApp || 'index.html'}?${params.toString()}`;
|
const urlPath = `./trace/${options.webApp || 'index.html'}?${params.toString()}`;
|
||||||
server.routePath('/', (_, response) => {
|
server.routePath('/', (_, response) => {
|
||||||
response.statusCode = 302;
|
response.statusCode = 302;
|
||||||
response.setHeader('Location', urlPath);
|
response.setHeader('Location', urlPath);
|
||||||
|
|
@ -145,7 +148,7 @@ export async function runTraceViewerApp(traceUrls: string[], browserName: string
|
||||||
validateTraceUrls(traceUrls);
|
validateTraceUrls(traceUrls);
|
||||||
const server = await startTraceViewerServer(options);
|
const server = await startTraceViewerServer(options);
|
||||||
await installRootRedirect(server, traceUrls, options);
|
await installRootRedirect(server, traceUrls, options);
|
||||||
const page = await openTraceViewerApp(server.urlPrefix(), browserName, options);
|
const page = await openTraceViewerApp(server.urlPrefix('precise'), browserName, options);
|
||||||
if (exitOnClose)
|
if (exitOnClose)
|
||||||
page.on('close', () => gracefullyProcessExitDoNotHang(0));
|
page.on('close', () => gracefullyProcessExitDoNotHang(0));
|
||||||
return page;
|
return page;
|
||||||
|
|
@ -155,7 +158,7 @@ export async function runTraceInBrowser(traceUrls: string[], options: TraceViewe
|
||||||
validateTraceUrls(traceUrls);
|
validateTraceUrls(traceUrls);
|
||||||
const server = await startTraceViewerServer(options);
|
const server = await startTraceViewerServer(options);
|
||||||
await installRootRedirect(server, traceUrls, options);
|
await installRootRedirect(server, traceUrls, options);
|
||||||
await openTraceInBrowser(server.urlPrefix());
|
await openTraceInBrowser(server.urlPrefix('human-readable'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openTraceViewerApp(url: string, browserName: string, options?: TraceViewerAppOptions): Promise<Page> {
|
export async function openTraceViewerApp(url: string, browserName: string, options?: TraceViewerAppOptions): Promise<Page> {
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ export class WKRouteImpl implements network.RouteDelegate {
|
||||||
await this._session.sendMayFail('Network.interceptRequestWithResponse', {
|
await this._session.sendMayFail('Network.interceptRequestWithResponse', {
|
||||||
requestId: this._requestId,
|
requestId: this._requestId,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
statusText: network.STATUS_TEXTS[String(response.status)],
|
statusText: network.statusText(response.status),
|
||||||
mimeType,
|
mimeType,
|
||||||
headers,
|
headers,
|
||||||
base64Encoded: response.isBase64,
|
base64Encoded: response.isBase64,
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,13 @@ export function getFromENV(name: string): string | undefined {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAsBooleanFromENV(name: string): boolean {
|
export function getAsBooleanFromENV(name: string, defaultValue?: boolean | undefined): boolean {
|
||||||
const value = getFromENV(name);
|
const value = getFromENV(name);
|
||||||
return !!value && value !== 'false' && value !== '0';
|
if (value === 'false' || value === '0')
|
||||||
|
return false;
|
||||||
|
if (value)
|
||||||
|
return true;
|
||||||
|
return !!defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPackageManager() {
|
export function getPackageManager() {
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ export type Transport = {
|
||||||
|
|
||||||
export class HttpServer {
|
export class HttpServer {
|
||||||
private _server: http.Server;
|
private _server: http.Server;
|
||||||
private _urlPrefix: string;
|
private _urlPrefixPrecise: string = '';
|
||||||
|
private _urlPrefixHumanReadable: string = '';
|
||||||
private _port: number = 0;
|
private _port: number = 0;
|
||||||
private _started = false;
|
private _started = false;
|
||||||
private _routes: { prefix?: string, exact?: string, handler: ServerRouteHandler }[] = [];
|
private _routes: { prefix?: string, exact?: string, handler: ServerRouteHandler }[] = [];
|
||||||
private _wsGuid: string | undefined;
|
private _wsGuid: string | undefined;
|
||||||
|
|
||||||
constructor(address: string = '') {
|
constructor() {
|
||||||
this._urlPrefix = address;
|
|
||||||
this._server = createHttpServer(this._onRequest.bind(this));
|
this._server = createHttpServer(this._onRequest.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ export class HttpServer {
|
||||||
return this._wsGuid;
|
return this._wsGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(options: { port?: number, preferredPort?: number, host?: string } = {}): Promise<string> {
|
async start(options: { port?: number, preferredPort?: number, host?: string } = {}): Promise<void> {
|
||||||
assert(!this._started, 'server already started');
|
assert(!this._started, 'server already started');
|
||||||
this._started = true;
|
this._started = true;
|
||||||
|
|
||||||
|
|
@ -121,24 +121,23 @@ export class HttpServer {
|
||||||
|
|
||||||
const address = this._server.address();
|
const address = this._server.address();
|
||||||
assert(address, 'Could not bind server socket');
|
assert(address, 'Could not bind server socket');
|
||||||
if (!this._urlPrefix) {
|
if (typeof address === 'string') {
|
||||||
if (typeof address === 'string') {
|
this._urlPrefixPrecise = address;
|
||||||
this._urlPrefix = address;
|
this._urlPrefixHumanReadable = address;
|
||||||
} else {
|
} else {
|
||||||
this._port = address.port;
|
this._port = address.port;
|
||||||
const resolvedHost = address.family === 'IPv4' ? address.address : `[${address.address}]`;
|
const resolvedHost = address.family === 'IPv4' ? address.address : `[${address.address}]`;
|
||||||
this._urlPrefix = `http://${resolvedHost}:${address.port}`;
|
this._urlPrefixPrecise = `http://${resolvedHost}:${address.port}`;
|
||||||
}
|
this._urlPrefixHumanReadable = `http://${host}:${address.port}`;
|
||||||
}
|
}
|
||||||
return this._urlPrefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop() {
|
async stop() {
|
||||||
await new Promise(cb => this._server!.close(cb));
|
await new Promise(cb => this._server!.close(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
urlPrefix(): string {
|
urlPrefix(purpose: 'human-readable' | 'precise'): string {
|
||||||
return this._urlPrefix;
|
return purpose === 'human-readable' ? this._urlPrefixHumanReadable : this._urlPrefixPrecise;
|
||||||
}
|
}
|
||||||
|
|
||||||
serveFile(request: http.IncomingMessage, response: http.ServerResponse, absoluteFilePath: string, headers?: { [name: string]: string }): boolean {
|
serveFile(request: http.IncomingMessage, response: http.ServerResponse, absoluteFilePath: string, headers?: { [name: string]: string }): boolean {
|
||||||
|
|
|
||||||
137
packages/playwright-core/types/protocol.d.ts
vendored
137
packages/playwright-core/types/protocol.d.ts
vendored
|
|
@ -841,6 +841,7 @@ CORS RFC1918 enforcement.
|
||||||
clientSecurityState?: Network.ClientSecurityState;
|
clientSecurityState?: Network.ClientSecurityState;
|
||||||
}
|
}
|
||||||
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader";
|
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader";
|
||||||
|
export type SharedDictionaryError = "UseErrorCrossOriginNoCorsRequest"|"UseErrorDictionaryLoadFailure"|"UseErrorMatchingDictionaryNotUsed"|"UseErrorUnexpectedContentDictionaryHeader"|"WriteErrorCossOriginNoCorsRequest"|"WriteErrorDisallowedBySettings"|"WriteErrorExpiredResponse"|"WriteErrorFeatureDisabled"|"WriteErrorInsufficientResources"|"WriteErrorInvalidMatchField"|"WriteErrorInvalidStructuredHeader"|"WriteErrorNavigationRequest"|"WriteErrorNoMatchField"|"WriteErrorNonListMatchDestField"|"WriteErrorNonSecureContext"|"WriteErrorNonStringIdField"|"WriteErrorNonStringInMatchDestList"|"WriteErrorNonStringMatchField"|"WriteErrorNonTokenTypeField"|"WriteErrorRequestAborted"|"WriteErrorShuttingDown"|"WriteErrorTooLongIdField"|"WriteErrorUnsupportedType";
|
||||||
/**
|
/**
|
||||||
* Details for issues around "Attribution Reporting API" usage.
|
* Details for issues around "Attribution Reporting API" usage.
|
||||||
Explainer: https://github.com/WICG/attribution-reporting-api
|
Explainer: https://github.com/WICG/attribution-reporting-api
|
||||||
|
|
@ -870,6 +871,10 @@ instead of "limited-quirks".
|
||||||
url: string;
|
url: string;
|
||||||
location?: SourceCodeLocation;
|
location?: SourceCodeLocation;
|
||||||
}
|
}
|
||||||
|
export interface SharedDictionaryIssueDetails {
|
||||||
|
sharedDictionaryError: SharedDictionaryError;
|
||||||
|
request: AffectedRequest;
|
||||||
|
}
|
||||||
export type GenericIssueErrorType = "CrossOriginPortalPostMessageError"|"FormLabelForNameError"|"FormDuplicateIdForInputError"|"FormInputWithNoLabelError"|"FormAutocompleteAttributeEmptyError"|"FormEmptyIdAndNameAttributesForInputError"|"FormAriaLabelledByToNonExistingId"|"FormInputAssignedAutocompleteValueToIdOrNameAttributeError"|"FormLabelHasNeitherForNorNestedInput"|"FormLabelForMatchesNonExistingIdError"|"FormInputHasWrongButWellIntendedAutocompleteValueError"|"ResponseWasBlockedByORB";
|
export type GenericIssueErrorType = "CrossOriginPortalPostMessageError"|"FormLabelForNameError"|"FormDuplicateIdForInputError"|"FormInputWithNoLabelError"|"FormAutocompleteAttributeEmptyError"|"FormEmptyIdAndNameAttributesForInputError"|"FormAriaLabelledByToNonExistingId"|"FormInputAssignedAutocompleteValueToIdOrNameAttributeError"|"FormLabelHasNeitherForNorNestedInput"|"FormLabelForMatchesNonExistingIdError"|"FormInputHasWrongButWellIntendedAutocompleteValueError"|"ResponseWasBlockedByORB";
|
||||||
/**
|
/**
|
||||||
* Depending on the concrete errorType, different properties are set.
|
* Depending on the concrete errorType, different properties are set.
|
||||||
|
|
@ -997,7 +1002,7 @@ registrations being ignored.
|
||||||
optional fields in InspectorIssueDetails to convey more specific
|
optional fields in InspectorIssueDetails to convey more specific
|
||||||
information about the kind of issue.
|
information about the kind of issue.
|
||||||
*/
|
*/
|
||||||
export type InspectorIssueCode = "CookieIssue"|"MixedContentIssue"|"BlockedByResponseIssue"|"HeavyAdIssue"|"ContentSecurityPolicyIssue"|"SharedArrayBufferIssue"|"LowTextContrastIssue"|"CorsIssue"|"AttributionReportingIssue"|"QuirksModeIssue"|"NavigatorUserAgentIssue"|"GenericIssue"|"DeprecationIssue"|"ClientHintIssue"|"FederatedAuthRequestIssue"|"BounceTrackingIssue"|"CookieDeprecationMetadataIssue"|"StylesheetLoadingIssue"|"FederatedAuthUserInfoRequestIssue"|"PropertyRuleIssue";
|
export type InspectorIssueCode = "CookieIssue"|"MixedContentIssue"|"BlockedByResponseIssue"|"HeavyAdIssue"|"ContentSecurityPolicyIssue"|"SharedArrayBufferIssue"|"LowTextContrastIssue"|"CorsIssue"|"AttributionReportingIssue"|"QuirksModeIssue"|"NavigatorUserAgentIssue"|"GenericIssue"|"DeprecationIssue"|"ClientHintIssue"|"FederatedAuthRequestIssue"|"BounceTrackingIssue"|"CookieDeprecationMetadataIssue"|"StylesheetLoadingIssue"|"FederatedAuthUserInfoRequestIssue"|"PropertyRuleIssue"|"SharedDictionaryIssue";
|
||||||
/**
|
/**
|
||||||
* This struct holds a list of optional fields with additional information
|
* This struct holds a list of optional fields with additional information
|
||||||
specific to the kind of issue. When adding a new issue code, please also
|
specific to the kind of issue. When adding a new issue code, please also
|
||||||
|
|
@ -1024,6 +1029,7 @@ add a new optional field to this type.
|
||||||
stylesheetLoadingIssueDetails?: StylesheetLoadingIssueDetails;
|
stylesheetLoadingIssueDetails?: StylesheetLoadingIssueDetails;
|
||||||
propertyRuleIssueDetails?: PropertyRuleIssueDetails;
|
propertyRuleIssueDetails?: PropertyRuleIssueDetails;
|
||||||
federatedAuthUserInfoRequestIssueDetails?: FederatedAuthUserInfoRequestIssueDetails;
|
federatedAuthUserInfoRequestIssueDetails?: FederatedAuthUserInfoRequestIssueDetails;
|
||||||
|
sharedDictionaryIssueDetails?: SharedDictionaryIssueDetails;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* A unique id for a DevTools inspector issue. Allows other entities (e.g.
|
* A unique id for a DevTools inspector issue. Allows other entities (e.g.
|
||||||
|
|
@ -1121,6 +1127,33 @@ using Audits.issueAdded event.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines commands and events for browser extensions. Available if the client
|
||||||
|
is connected using the --remote-debugging-pipe flag and
|
||||||
|
the --enable-unsafe-extension-debugging flag is set.
|
||||||
|
*/
|
||||||
|
export module Extensions {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs an unpacked extension from the filesystem similar to
|
||||||
|
--load-extension CLI flags. Returns extension ID once the extension
|
||||||
|
has been installed.
|
||||||
|
*/
|
||||||
|
export type loadUnpackedParameters = {
|
||||||
|
/**
|
||||||
|
* Absolute file path.
|
||||||
|
*/
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
export type loadUnpackedReturnValue = {
|
||||||
|
/**
|
||||||
|
* Extension id.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines commands and events for Autofill.
|
* Defines commands and events for Autofill.
|
||||||
*/
|
*/
|
||||||
|
|
@ -3450,7 +3483,7 @@ front-end.
|
||||||
/**
|
/**
|
||||||
* Pseudo element type.
|
* Pseudo element type.
|
||||||
*/
|
*/
|
||||||
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-markers"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
|
export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-markers"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new";
|
||||||
/**
|
/**
|
||||||
* Shadow root type.
|
* Shadow root type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -4426,6 +4459,25 @@ appear on top of all other content.
|
||||||
*/
|
*/
|
||||||
nodeIds: NodeId[];
|
nodeIds: NodeId[];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns the NodeId of the matched element according to certain relations.
|
||||||
|
*/
|
||||||
|
export type getElementByRelationParameters = {
|
||||||
|
/**
|
||||||
|
* Id of the node from which to query the relation.
|
||||||
|
*/
|
||||||
|
nodeId: NodeId;
|
||||||
|
/**
|
||||||
|
* Type of relation to get.
|
||||||
|
*/
|
||||||
|
relation: "PopoverTarget";
|
||||||
|
}
|
||||||
|
export type getElementByRelationReturnValue = {
|
||||||
|
/**
|
||||||
|
* NodeId of the element matching the queried relation.
|
||||||
|
*/
|
||||||
|
nodeId: NodeId;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Re-does the last undone action.
|
* Re-does the last undone action.
|
||||||
*/
|
*/
|
||||||
|
|
@ -8309,8 +8361,16 @@ records.
|
||||||
*/
|
*/
|
||||||
export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler";
|
export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler";
|
||||||
export interface ServiceWorkerRouterInfo {
|
export interface ServiceWorkerRouterInfo {
|
||||||
ruleIdMatched: number;
|
/**
|
||||||
matchedSourceType: ServiceWorkerRouterSource;
|
* ID of the rule matched. If there is a matched rule, this field will
|
||||||
|
be set, otherwiser no value will be set.
|
||||||
|
*/
|
||||||
|
ruleIdMatched?: number;
|
||||||
|
/**
|
||||||
|
* The router source of the matched rule. If there is a matched rule, this
|
||||||
|
field will be set, otherwise no value will be set.
|
||||||
|
*/
|
||||||
|
matchedSourceType?: ServiceWorkerRouterSource;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* HTTP response data.
|
* HTTP response data.
|
||||||
|
|
@ -8385,7 +8445,10 @@ records.
|
||||||
*/
|
*/
|
||||||
fromEarlyHints?: boolean;
|
fromEarlyHints?: boolean;
|
||||||
/**
|
/**
|
||||||
* Information about how Service Worker Static Router was used.
|
* Information about how ServiceWorker Static Router API was used. If this
|
||||||
|
field is set with `matchedSourceType` field, a matching rule is found.
|
||||||
|
If this field is set without `matchedSource`, no matching rule is found.
|
||||||
|
Otherwise, the API is not used.
|
||||||
*/
|
*/
|
||||||
serviceWorkerRouterInfo?: ServiceWorkerRouterInfo;
|
serviceWorkerRouterInfo?: ServiceWorkerRouterInfo;
|
||||||
/**
|
/**
|
||||||
|
|
@ -11167,7 +11230,7 @@ as an ad.
|
||||||
* All Permissions Policy features. This enum should match the one defined
|
* All Permissions Policy features. This enum should match the one defined
|
||||||
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
||||||
*/
|
*/
|
||||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"window-placement"|"xr-spatial-tracking";
|
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking";
|
||||||
/**
|
/**
|
||||||
* Reason for a permissions policy feature to be disabled.
|
* Reason for a permissions policy feature to be disabled.
|
||||||
*/
|
*/
|
||||||
|
|
@ -11755,7 +11818,7 @@ https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-expl
|
||||||
/**
|
/**
|
||||||
* List of not restored reasons for back-forward cache.
|
* List of not restored reasons for back-forward cache.
|
||||||
*/
|
*/
|
||||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient";
|
||||||
/**
|
/**
|
||||||
* Types of not restored reasons for back-forward cache.
|
* Types of not restored reasons for back-forward cache.
|
||||||
*/
|
*/
|
||||||
|
|
@ -12334,7 +12397,7 @@ option, use with caution.
|
||||||
This API always waits for the manifest to be loaded.
|
This API always waits for the manifest to be loaded.
|
||||||
If manifestId is provided, and it does not match the manifest of the
|
If manifestId is provided, and it does not match the manifest of the
|
||||||
current document, this API errors out.
|
current document, this API errors out.
|
||||||
If there isn’t a loaded page, this API errors out immediately.
|
If there is not a loaded page, this API errors out immediately.
|
||||||
*/
|
*/
|
||||||
export type getAppManifestParameters = {
|
export type getAppManifestParameters = {
|
||||||
manifestId?: string;
|
manifestId?: string;
|
||||||
|
|
@ -16690,7 +16753,7 @@ possible for multiple rule sets and links to trigger a single attempt.
|
||||||
/**
|
/**
|
||||||
* List of FinalStatus reasons for Prerender2.
|
* List of FinalStatus reasons for Prerender2.
|
||||||
*/
|
*/
|
||||||
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl";
|
export type PrerenderFinalStatus = "Activated"|"Destroyed"|"LowEndDevice"|"InvalidSchemeRedirect"|"InvalidSchemeNavigation"|"NavigationRequestBlockedByCsp"|"MainFrameNavigation"|"MojoBinderPolicy"|"RendererProcessCrashed"|"RendererProcessKilled"|"Download"|"TriggerDestroyed"|"NavigationNotCommitted"|"NavigationBadHttpStatus"|"ClientCertRequested"|"NavigationRequestNetworkError"|"CancelAllHostsForTesting"|"DidFailLoad"|"Stop"|"SslCertificateError"|"LoginAuthRequested"|"UaChangeRequiresReload"|"BlockedByClient"|"AudioOutputDeviceRequested"|"MixedContent"|"TriggerBackgrounded"|"MemoryLimitExceeded"|"DataSaverEnabled"|"TriggerUrlHasEffectiveUrl"|"ActivatedBeforeStarted"|"InactivePageRestriction"|"StartFailed"|"TimeoutBackgrounded"|"CrossSiteRedirectInInitialNavigation"|"CrossSiteNavigationInInitialNavigation"|"SameSiteCrossOriginRedirectNotOptInInInitialNavigation"|"SameSiteCrossOriginNavigationNotOptInInInitialNavigation"|"ActivationNavigationParameterMismatch"|"ActivatedInBackground"|"EmbedderHostDisallowed"|"ActivationNavigationDestroyedBeforeSuccess"|"TabClosedByUserGesture"|"TabClosedWithoutUserGesture"|"PrimaryMainFrameRendererProcessCrashed"|"PrimaryMainFrameRendererProcessKilled"|"ActivationFramePolicyNotCompatible"|"PreloadingDisabled"|"BatterySaverEnabled"|"ActivatedDuringMainFrameNavigation"|"PreloadingUnsupportedByWebContents"|"CrossSiteRedirectInMainFrameNavigation"|"CrossSiteNavigationInMainFrameNavigation"|"SameSiteCrossOriginRedirectNotOptInInMainFrameNavigation"|"SameSiteCrossOriginNavigationNotOptInInMainFrameNavigation"|"MemoryPressureOnTrigger"|"MemoryPressureAfterTriggered"|"PrerenderingDisabledByDevTools"|"SpeculationRuleRemoved"|"ActivatedWithAuxiliaryBrowsingContexts"|"MaxNumOfRunningEagerPrerendersExceeded"|"MaxNumOfRunningNonEagerPrerendersExceeded"|"MaxNumOfRunningEmbedderPrerendersExceeded"|"PrerenderingUrlHasEffectiveUrl"|"RedirectedPrerenderingUrlHasEffectiveUrl"|"ActivationUrlHasEffectiveUrl"|"JavaScriptInterfaceAdded"|"JavaScriptInterfaceRemoved"|"AllPrerenderingCanceled";
|
||||||
/**
|
/**
|
||||||
* Preloading status values, see also PreloadingTriggeringOutcome. This
|
* Preloading status values, see also PreloadingTriggeringOutcome. This
|
||||||
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
|
status is shared by prefetchStatusUpdated and prerenderStatusUpdated.
|
||||||
|
|
@ -16921,6 +16984,52 @@ https://web.dev/learn/pwa/web-app-manifest.
|
||||||
badgeCount: number;
|
badgeCount: number;
|
||||||
fileHandlers: FileHandler[];
|
fileHandlers: FileHandler[];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Installs the given manifest identity, optionally using the given install_url
|
||||||
|
or IWA bundle location.
|
||||||
|
|
||||||
|
TODO(crbug.com/337872319) Support IWA to meet the following specific
|
||||||
|
requirement.
|
||||||
|
IWA-specific install description: If the manifest_id is isolated-app://,
|
||||||
|
install_url_or_bundle_url is required, and can be either an http(s) URL or
|
||||||
|
file:// URL pointing to a signed web bundle (.swbn). The .swbn file's
|
||||||
|
signing key must correspond to manifest_id. If Chrome is not in IWA dev
|
||||||
|
mode, the installation will fail, regardless of the state of the allowlist.
|
||||||
|
*/
|
||||||
|
export type installParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
/**
|
||||||
|
* The location of the app or bundle overriding the one derived from the
|
||||||
|
manifestId.
|
||||||
|
*/
|
||||||
|
installUrlOrBundleUrl?: string;
|
||||||
|
}
|
||||||
|
export type installReturnValue = {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Uninstals the given manifest_id and closes any opened app windows.
|
||||||
|
*/
|
||||||
|
export type uninstallParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
}
|
||||||
|
export type uninstallReturnValue = {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Launches the installed web app, or an url in the same web app instead of the
|
||||||
|
default start url if it is provided. Returns a tab / web contents based
|
||||||
|
Target.TargetID which can be used to attach to via Target.attachToTarget or
|
||||||
|
similar APIs.
|
||||||
|
*/
|
||||||
|
export type launchParameters = {
|
||||||
|
manifestId: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
export type launchReturnValue = {
|
||||||
|
/**
|
||||||
|
* ID of the tab target created as a result.
|
||||||
|
*/
|
||||||
|
targetId: Target.TargetID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -19773,6 +19882,7 @@ Error was thrown.
|
||||||
"Audits.enable": Audits.enableParameters;
|
"Audits.enable": Audits.enableParameters;
|
||||||
"Audits.checkContrast": Audits.checkContrastParameters;
|
"Audits.checkContrast": Audits.checkContrastParameters;
|
||||||
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
|
"Audits.checkFormsIssues": Audits.checkFormsIssuesParameters;
|
||||||
|
"Extensions.loadUnpacked": Extensions.loadUnpackedParameters;
|
||||||
"Autofill.trigger": Autofill.triggerParameters;
|
"Autofill.trigger": Autofill.triggerParameters;
|
||||||
"Autofill.setAddresses": Autofill.setAddressesParameters;
|
"Autofill.setAddresses": Autofill.setAddressesParameters;
|
||||||
"Autofill.disable": Autofill.disableParameters;
|
"Autofill.disable": Autofill.disableParameters;
|
||||||
|
|
@ -19870,6 +19980,7 @@ Error was thrown.
|
||||||
"DOM.querySelector": DOM.querySelectorParameters;
|
"DOM.querySelector": DOM.querySelectorParameters;
|
||||||
"DOM.querySelectorAll": DOM.querySelectorAllParameters;
|
"DOM.querySelectorAll": DOM.querySelectorAllParameters;
|
||||||
"DOM.getTopLayerElements": DOM.getTopLayerElementsParameters;
|
"DOM.getTopLayerElements": DOM.getTopLayerElementsParameters;
|
||||||
|
"DOM.getElementByRelation": DOM.getElementByRelationParameters;
|
||||||
"DOM.redo": DOM.redoParameters;
|
"DOM.redo": DOM.redoParameters;
|
||||||
"DOM.removeAttribute": DOM.removeAttributeParameters;
|
"DOM.removeAttribute": DOM.removeAttributeParameters;
|
||||||
"DOM.removeNode": DOM.removeNodeParameters;
|
"DOM.removeNode": DOM.removeNodeParameters;
|
||||||
|
|
@ -20256,6 +20367,9 @@ Error was thrown.
|
||||||
"FedCm.dismissDialog": FedCm.dismissDialogParameters;
|
"FedCm.dismissDialog": FedCm.dismissDialogParameters;
|
||||||
"FedCm.resetCooldown": FedCm.resetCooldownParameters;
|
"FedCm.resetCooldown": FedCm.resetCooldownParameters;
|
||||||
"PWA.getOsAppState": PWA.getOsAppStateParameters;
|
"PWA.getOsAppState": PWA.getOsAppStateParameters;
|
||||||
|
"PWA.install": PWA.installParameters;
|
||||||
|
"PWA.uninstall": PWA.uninstallParameters;
|
||||||
|
"PWA.launch": PWA.launchParameters;
|
||||||
"Console.clearMessages": Console.clearMessagesParameters;
|
"Console.clearMessages": Console.clearMessagesParameters;
|
||||||
"Console.disable": Console.disableParameters;
|
"Console.disable": Console.disableParameters;
|
||||||
"Console.enable": Console.enableParameters;
|
"Console.enable": Console.enableParameters;
|
||||||
|
|
@ -20361,6 +20475,7 @@ Error was thrown.
|
||||||
"Audits.enable": Audits.enableReturnValue;
|
"Audits.enable": Audits.enableReturnValue;
|
||||||
"Audits.checkContrast": Audits.checkContrastReturnValue;
|
"Audits.checkContrast": Audits.checkContrastReturnValue;
|
||||||
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
|
"Audits.checkFormsIssues": Audits.checkFormsIssuesReturnValue;
|
||||||
|
"Extensions.loadUnpacked": Extensions.loadUnpackedReturnValue;
|
||||||
"Autofill.trigger": Autofill.triggerReturnValue;
|
"Autofill.trigger": Autofill.triggerReturnValue;
|
||||||
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
|
"Autofill.setAddresses": Autofill.setAddressesReturnValue;
|
||||||
"Autofill.disable": Autofill.disableReturnValue;
|
"Autofill.disable": Autofill.disableReturnValue;
|
||||||
|
|
@ -20458,6 +20573,7 @@ Error was thrown.
|
||||||
"DOM.querySelector": DOM.querySelectorReturnValue;
|
"DOM.querySelector": DOM.querySelectorReturnValue;
|
||||||
"DOM.querySelectorAll": DOM.querySelectorAllReturnValue;
|
"DOM.querySelectorAll": DOM.querySelectorAllReturnValue;
|
||||||
"DOM.getTopLayerElements": DOM.getTopLayerElementsReturnValue;
|
"DOM.getTopLayerElements": DOM.getTopLayerElementsReturnValue;
|
||||||
|
"DOM.getElementByRelation": DOM.getElementByRelationReturnValue;
|
||||||
"DOM.redo": DOM.redoReturnValue;
|
"DOM.redo": DOM.redoReturnValue;
|
||||||
"DOM.removeAttribute": DOM.removeAttributeReturnValue;
|
"DOM.removeAttribute": DOM.removeAttributeReturnValue;
|
||||||
"DOM.removeNode": DOM.removeNodeReturnValue;
|
"DOM.removeNode": DOM.removeNodeReturnValue;
|
||||||
|
|
@ -20844,6 +20960,9 @@ Error was thrown.
|
||||||
"FedCm.dismissDialog": FedCm.dismissDialogReturnValue;
|
"FedCm.dismissDialog": FedCm.dismissDialogReturnValue;
|
||||||
"FedCm.resetCooldown": FedCm.resetCooldownReturnValue;
|
"FedCm.resetCooldown": FedCm.resetCooldownReturnValue;
|
||||||
"PWA.getOsAppState": PWA.getOsAppStateReturnValue;
|
"PWA.getOsAppState": PWA.getOsAppStateReturnValue;
|
||||||
|
"PWA.install": PWA.installReturnValue;
|
||||||
|
"PWA.uninstall": PWA.uninstallReturnValue;
|
||||||
|
"PWA.launch": PWA.launchReturnValue;
|
||||||
"Console.clearMessages": Console.clearMessagesReturnValue;
|
"Console.clearMessages": Console.clearMessagesReturnValue;
|
||||||
"Console.disable": Console.disableReturnValue;
|
"Console.disable": Console.disableReturnValue;
|
||||||
"Console.enable": Console.enableReturnValue;
|
"Console.enable": Console.enableReturnValue;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export type FixturesWithLocation = {
|
||||||
fixtures: Fixtures;
|
fixtures: Fixtures;
|
||||||
location: Location;
|
location: Location;
|
||||||
};
|
};
|
||||||
export type Annotation = { type: string, description?: string };
|
export type Annotation = { type: string, description?: string, url?: string };
|
||||||
|
|
||||||
export const defaultTimeout = 30000;
|
export const defaultTimeout = 30000;
|
||||||
|
|
||||||
|
|
@ -52,6 +52,7 @@ export class FullConfigInternal {
|
||||||
cliProjectFilter?: string[];
|
cliProjectFilter?: string[];
|
||||||
cliListOnly = false;
|
cliListOnly = false;
|
||||||
cliPassWithNoTests?: boolean;
|
cliPassWithNoTests?: boolean;
|
||||||
|
cliFailOnFlakyTests?: boolean;
|
||||||
testIdMatcher?: Matcher;
|
testIdMatcher?: Matcher;
|
||||||
defineConfigWasUsed = false;
|
defineConfigWasUsed = false;
|
||||||
|
|
||||||
|
|
@ -266,7 +267,7 @@ export function toReporters(reporters: BuiltInReporter | ReporterDescription[] |
|
||||||
export const builtInReporters = ['list', 'line', 'dot', 'json', 'junit', 'null', 'github', 'html', 'blob', 'markdown'] as const;
|
export const builtInReporters = ['list', 'line', 'dot', 'json', 'junit', 'null', 'github', 'html', 'blob', 'markdown'] as const;
|
||||||
export type BuiltInReporter = typeof builtInReporters[number];
|
export type BuiltInReporter = typeof builtInReporters[number];
|
||||||
|
|
||||||
export type ContextReuseMode = 'none' | 'force' | 'when-possible';
|
export type ContextReuseMode = 'none' | 'when-possible';
|
||||||
|
|
||||||
function resolveScript(id: string | undefined, rootDir: string): string | undefined {
|
function resolveScript(id: string | undefined, rootDir: string): string | undefined {
|
||||||
if (!id)
|
if (!id)
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,12 @@ export class ProcessRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let closed = false;
|
let gracefullyCloseCalled = false;
|
||||||
|
let forceExitInitiated = false;
|
||||||
|
|
||||||
sendMessageToParent({ method: 'ready' });
|
sendMessageToParent({ method: 'ready' });
|
||||||
|
|
||||||
process.on('disconnect', gracefullyCloseAndExit);
|
process.on('disconnect', () => gracefullyCloseAndExit(true));
|
||||||
process.on('SIGINT', () => {});
|
process.on('SIGINT', () => {});
|
||||||
process.on('SIGTERM', () => {});
|
process.on('SIGTERM', () => {});
|
||||||
|
|
||||||
|
|
@ -76,7 +77,7 @@ process.on('message', async (message: any) => {
|
||||||
const keys = new Set([...Object.keys(process.env), ...Object.keys(startingEnv)]);
|
const keys = new Set([...Object.keys(process.env), ...Object.keys(startingEnv)]);
|
||||||
const producedEnv: EnvProducedPayload = [...keys].filter(key => startingEnv[key] !== process.env[key]).map(key => [key, process.env[key] ?? null]);
|
const producedEnv: EnvProducedPayload = [...keys].filter(key => startingEnv[key] !== process.env[key]).map(key => [key, process.env[key] ?? null]);
|
||||||
sendMessageToParent({ method: '__env_produced__', params: producedEnv });
|
sendMessageToParent({ method: '__env_produced__', params: producedEnv });
|
||||||
await gracefullyCloseAndExit();
|
await gracefullyCloseAndExit(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.method === '__dispatch__') {
|
if (message.method === '__dispatch__') {
|
||||||
|
|
@ -92,19 +93,24 @@ process.on('message', async (message: any) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function gracefullyCloseAndExit() {
|
const kForceExitTimeout = +(process.env.PWTEST_FORCE_EXIT_TIMEOUT || 30000);
|
||||||
if (closed)
|
|
||||||
return;
|
async function gracefullyCloseAndExit(forceExit: boolean) {
|
||||||
closed = true;
|
if (forceExit && !forceExitInitiated) {
|
||||||
// Force exit after 30 seconds.
|
forceExitInitiated = true;
|
||||||
// eslint-disable-next-line no-restricted-properties
|
// Force exit after 30 seconds.
|
||||||
setTimeout(() => process.exit(0), 30000);
|
// eslint-disable-next-line no-restricted-properties
|
||||||
// Meanwhile, try to gracefully shutdown.
|
setTimeout(() => process.exit(0), kForceExitTimeout);
|
||||||
await processRunner?.gracefullyClose().catch(() => {});
|
}
|
||||||
if (processName)
|
if (!gracefullyCloseCalled) {
|
||||||
await stopProfiling(processName).catch(() => {});
|
gracefullyCloseCalled = true;
|
||||||
// eslint-disable-next-line no-restricted-properties
|
// Meanwhile, try to gracefully shutdown.
|
||||||
process.exit(0);
|
await processRunner?.gracefullyClose().catch(() => {});
|
||||||
|
if (processName)
|
||||||
|
await stopProfiling(processName).catch(() => {});
|
||||||
|
// eslint-disable-next-line no-restricted-properties
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessageToParent(message: { method: string, params?: any }) {
|
function sendMessageToParent(message: { method: string, params?: any }) {
|
||||||
|
|
|
||||||
|
|
@ -356,8 +356,8 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
_reuseContext: [async ({ video, _optionContextReuseMode }, use) => {
|
_reuseContext: [async ({ video, _optionContextReuseMode }, use) => {
|
||||||
let mode = _optionContextReuseMode;
|
let mode = _optionContextReuseMode;
|
||||||
if (process.env.PW_TEST_REUSE_CONTEXT)
|
if (process.env.PW_TEST_REUSE_CONTEXT)
|
||||||
mode = process.env.PW_TEST_REUSE_CONTEXT === 'when-possible' ? 'when-possible' : (process.env.PW_TEST_REUSE_CONTEXT ? 'force' : 'none');
|
mode = 'when-possible';
|
||||||
const reuse = mode === 'force' || (mode === 'when-possible' && normalizeVideoMode(video) === 'off');
|
const reuse = mode === 'when-possible' && normalizeVideoMode(video) === 'off';
|
||||||
await use(reuse);
|
await use(reuse);
|
||||||
}, { scope: 'worker', _title: 'context' } as any],
|
}, { scope: 'worker', _title: 'context' } as any],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,10 @@ export interface TestServerInterface {
|
||||||
headed?: boolean;
|
headed?: boolean;
|
||||||
workers?: number | string;
|
workers?: number | string;
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
|
outputDir?: string;
|
||||||
reporters?: string[],
|
reporters?: string[],
|
||||||
trace?: 'on' | 'off';
|
trace?: 'on' | 'off';
|
||||||
|
video?: 'on' | 'off';
|
||||||
projects?: string[];
|
projects?: string[];
|
||||||
reuseContext?: boolean;
|
reuseContext?: boolean;
|
||||||
connectWsEndpoint?: string;
|
connectWsEndpoint?: string;
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||||
reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined,
|
reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined,
|
||||||
workers: cliOverrides.workers,
|
workers: cliOverrides.workers,
|
||||||
timeout: cliOverrides.timeout,
|
timeout: cliOverrides.timeout,
|
||||||
|
outputDir: cliOverrides.outputDir,
|
||||||
});
|
});
|
||||||
await stopProfiling('runner');
|
await stopProfiling('runner');
|
||||||
if (status === 'restarted')
|
if (status === 'restarted')
|
||||||
|
|
@ -194,6 +195,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||||
config.cliListOnly = !!opts.list;
|
config.cliListOnly = !!opts.list;
|
||||||
config.cliProjectFilter = opts.project || undefined;
|
config.cliProjectFilter = opts.project || undefined;
|
||||||
config.cliPassWithNoTests = !!opts.passWithNoTests;
|
config.cliPassWithNoTests = !!opts.passWithNoTests;
|
||||||
|
config.cliFailOnFlakyTests = !!opts.failOnFlakyTests;
|
||||||
|
|
||||||
const runner = new Runner(config);
|
const runner = new Runner(config);
|
||||||
let status: FullResult['status'];
|
let status: FullResult['status'];
|
||||||
|
|
@ -337,6 +339,7 @@ const testOptions: [string, string][] = [
|
||||||
['--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`],
|
['--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`],
|
||||||
['-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`],
|
['-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`],
|
||||||
['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`],
|
['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`],
|
||||||
|
['--fail-on-flaky-tests', `Fail if any test is flagged as flaky (default: false)`],
|
||||||
['--forbid-only', `Fail if test.only is called (default: false)`],
|
['--forbid-only', `Fail if test.only is called (default: false)`],
|
||||||
['--fully-parallel', `Run all tests in parallel (default: false)`],
|
['--fully-parallel', `Run all tests in parallel (default: false)`],
|
||||||
['--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`],
|
['--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`],
|
||||||
|
|
|
||||||
|
|
@ -45,27 +45,41 @@ type TestSummary = {
|
||||||
fatalErrors: TestError[];
|
fatalErrors: TestError[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isTTY = !!process.env.PWTEST_TTY_WIDTH || process.stdout.isTTY;
|
export const { isTTY, ttyWidth, colors } = (() => {
|
||||||
export const ttyWidth = process.env.PWTEST_TTY_WIDTH ? parseInt(process.env.PWTEST_TTY_WIDTH, 10) : process.stdout.columns || 0;
|
let isTTY = !!process.stdout.isTTY;
|
||||||
let useColors = isTTY;
|
let ttyWidth = process.stdout.columns || 0;
|
||||||
if (process.env.DEBUG_COLORS === '0'
|
if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {
|
||||||
|| process.env.DEBUG_COLORS === 'false'
|
isTTY = false;
|
||||||
|| process.env.FORCE_COLOR === '0'
|
ttyWidth = 0;
|
||||||
|| process.env.FORCE_COLOR === 'false')
|
} else if (process.env.PLAYWRIGHT_FORCE_TTY === 'true' || process.env.PLAYWRIGHT_FORCE_TTY === '1') {
|
||||||
useColors = false;
|
isTTY = true;
|
||||||
else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR)
|
ttyWidth = process.stdout.columns || 100;
|
||||||
useColors = true;
|
} else if (process.env.PLAYWRIGHT_FORCE_TTY) {
|
||||||
|
isTTY = true;
|
||||||
|
ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;
|
||||||
|
if (isNaN(ttyWidth))
|
||||||
|
ttyWidth = 100;
|
||||||
|
}
|
||||||
|
|
||||||
export const colors = useColors ? realColors : {
|
let useColors = isTTY;
|
||||||
bold: (t: string) => t,
|
if (process.env.DEBUG_COLORS === '0' || process.env.DEBUG_COLORS === 'false' ||
|
||||||
cyan: (t: string) => t,
|
process.env.FORCE_COLOR === '0' || process.env.FORCE_COLOR === 'false')
|
||||||
dim: (t: string) => t,
|
useColors = false;
|
||||||
gray: (t: string) => t,
|
else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR)
|
||||||
green: (t: string) => t,
|
useColors = true;
|
||||||
red: (t: string) => t,
|
|
||||||
yellow: (t: string) => t,
|
const colors = useColors ? realColors : {
|
||||||
enabled: false,
|
bold: (t: string) => t,
|
||||||
};
|
cyan: (t: string) => t,
|
||||||
|
dim: (t: string) => t,
|
||||||
|
gray: (t: string) => t,
|
||||||
|
green: (t: string) => t,
|
||||||
|
red: (t: string) => t,
|
||||||
|
yellow: (t: string) => t,
|
||||||
|
enabled: false,
|
||||||
|
};
|
||||||
|
return { isTTY, ttyWidth, colors };
|
||||||
|
})();
|
||||||
|
|
||||||
export class BaseReporter implements ReporterV2 {
|
export class BaseReporter implements ReporterV2 {
|
||||||
config!: FullConfig;
|
config!: FullConfig;
|
||||||
|
|
|
||||||
|
|
@ -62,14 +62,14 @@ class HtmlReporter extends EmptyReporter {
|
||||||
private _outputFolder!: string;
|
private _outputFolder!: string;
|
||||||
private _attachmentsBaseURL!: string;
|
private _attachmentsBaseURL!: string;
|
||||||
private _open: string | undefined;
|
private _open: string | undefined;
|
||||||
|
private _port: number | undefined;
|
||||||
|
private _host: string | undefined;
|
||||||
private _buildResult: { ok: boolean, singleTestId: string | undefined } | undefined;
|
private _buildResult: { ok: boolean, singleTestId: string | undefined } | undefined;
|
||||||
private _topLevelErrors: TestError[] = [];
|
private _topLevelErrors: TestError[] = [];
|
||||||
|
|
||||||
constructor(options: HtmlReporterOptions) {
|
constructor(options: HtmlReporterOptions) {
|
||||||
super();
|
super();
|
||||||
this._options = options;
|
this._options = options;
|
||||||
if (options._mode === 'test')
|
|
||||||
process.env.PW_HTML_REPORT = '1';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override printsToStdio() {
|
override printsToStdio() {
|
||||||
|
|
@ -81,9 +81,11 @@ class HtmlReporter extends EmptyReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
override onBegin(suite: Suite) {
|
override onBegin(suite: Suite) {
|
||||||
const { outputFolder, open, attachmentsBaseURL } = this._resolveOptions();
|
const { outputFolder, open, attachmentsBaseURL, host, port } = this._resolveOptions();
|
||||||
this._outputFolder = outputFolder;
|
this._outputFolder = outputFolder;
|
||||||
this._open = open;
|
this._open = open;
|
||||||
|
this._host = host;
|
||||||
|
this._port = port;
|
||||||
this._attachmentsBaseURL = attachmentsBaseURL;
|
this._attachmentsBaseURL = attachmentsBaseURL;
|
||||||
const reportedWarnings = new Set<string>();
|
const reportedWarnings = new Set<string>();
|
||||||
for (const project of this.config.projects) {
|
for (const project of this.config.projects) {
|
||||||
|
|
@ -104,12 +106,14 @@ class HtmlReporter extends EmptyReporter {
|
||||||
this.suite = suite;
|
this.suite = suite;
|
||||||
}
|
}
|
||||||
|
|
||||||
_resolveOptions(): { outputFolder: string, open: HtmlReportOpenOption, attachmentsBaseURL: string } {
|
_resolveOptions(): { outputFolder: string, open: HtmlReportOpenOption, attachmentsBaseURL: string, host: string | undefined, port: number | undefined } {
|
||||||
const outputFolder = reportFolderFromEnv() ?? resolveReporterOutputPath('playwright-report', this._options.configDir, this._options.outputFolder);
|
const outputFolder = reportFolderFromEnv() ?? resolveReporterOutputPath('playwright-report', this._options.configDir, this._options.outputFolder);
|
||||||
return {
|
return {
|
||||||
outputFolder,
|
outputFolder,
|
||||||
open: getHtmlReportOptionProcessEnv() || this._options.open || 'on-failure',
|
open: getHtmlReportOptionProcessEnv() || this._options.open || 'on-failure',
|
||||||
attachmentsBaseURL: this._options.attachmentsBaseURL || 'data/'
|
attachmentsBaseURL: process.env.PLAYWRIGHT_HTML_ATTACHMENTS_BASE_URL || this._options.attachmentsBaseURL || 'data/',
|
||||||
|
host: process.env.PLAYWRIGHT_HTML_HOST || this._options.host,
|
||||||
|
port: process.env.PLAYWRIGHT_HTML_PORT ? +process.env.PLAYWRIGHT_HTML_PORT : this._options.port,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,12 +139,12 @@ class HtmlReporter extends EmptyReporter {
|
||||||
const { ok, singleTestId } = this._buildResult;
|
const { ok, singleTestId } = this._buildResult;
|
||||||
const shouldOpen = !this._options._isTestServer && (this._open === 'always' || (!ok && this._open === 'on-failure'));
|
const shouldOpen = !this._options._isTestServer && (this._open === 'always' || (!ok && this._open === 'on-failure'));
|
||||||
if (shouldOpen) {
|
if (shouldOpen) {
|
||||||
await showHTMLReport(this._outputFolder, this._options.host, this._options.port, singleTestId);
|
await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId);
|
||||||
} else if (this._options._mode === 'test') {
|
} else if (this._options._mode === 'test') {
|
||||||
const packageManagerCommand = getPackageManagerExecCommand();
|
const packageManagerCommand = getPackageManagerExecCommand();
|
||||||
const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder);
|
const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder);
|
||||||
const hostArg = this._options.host ? ` --host ${this._options.host}` : '';
|
const hostArg = this._host ? ` --host ${this._host}` : '';
|
||||||
const portArg = this._options.port ? ` --port ${this._options.port}` : '';
|
const portArg = this._port ? ` --port ${this._port}` : '';
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('To open last HTML report run:');
|
console.log('To open last HTML report run:');
|
||||||
console.log(colors.cyan(`
|
console.log(colors.cyan(`
|
||||||
|
|
@ -151,18 +155,18 @@ class HtmlReporter extends EmptyReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportFolderFromEnv(): string | undefined {
|
function reportFolderFromEnv(): string | undefined {
|
||||||
if (process.env[`PLAYWRIGHT_HTML_REPORT`])
|
// Note: PLAYWRIGHT_HTML_REPORT is for backwards compatibility.
|
||||||
return path.resolve(process.cwd(), process.env[`PLAYWRIGHT_HTML_REPORT`]);
|
const envValue = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR || process.env.PLAYWRIGHT_HTML_REPORT;
|
||||||
return undefined;
|
return envValue ? path.resolve(envValue) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHtmlReportOptionProcessEnv(): HtmlReportOpenOption | undefined {
|
function getHtmlReportOptionProcessEnv(): HtmlReportOpenOption | undefined {
|
||||||
const processKey = 'PW_TEST_HTML_REPORT_OPEN';
|
// Note: PW_TEST_HTML_REPORT_OPEN is for backwards compatibility.
|
||||||
const htmlOpenEnv = process.env[processKey];
|
const htmlOpenEnv = process.env.PLAYWRIGHT_HTML_OPEN || process.env.PW_TEST_HTML_REPORT_OPEN;
|
||||||
if (!htmlOpenEnv)
|
if (!htmlOpenEnv)
|
||||||
return undefined;
|
return undefined;
|
||||||
if (!isHtmlReportOption(htmlOpenEnv)) {
|
if (!isHtmlReportOption(htmlOpenEnv)) {
|
||||||
console.log(colors.red(`Configuration Error: HTML reporter Invalid value for ${processKey}: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(', ')}`));
|
console.log(colors.red(`Configuration Error: HTML reporter Invalid value for PLAYWRIGHT_HTML_OPEN: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(', ')}`));
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return htmlOpenEnv;
|
return htmlOpenEnv;
|
||||||
|
|
@ -182,7 +186,8 @@ export async function showHTMLReport(reportFolder: string | undefined, host: str
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const server = startHtmlReportServer(folder);
|
const server = startHtmlReportServer(folder);
|
||||||
let url = await server.start({ port, host, preferredPort: port ? undefined : 9323 });
|
await server.start({ port, host, preferredPort: port ? undefined : 9323 });
|
||||||
|
let url = server.urlPrefix('human-readable');
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
console.log(colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
||||||
if (testId)
|
if (testId)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import path from 'path';
|
||||||
import type { FullConfig, FullResult, Suite, TestCase } from '../../types/testReporter';
|
import type { FullConfig, FullResult, Suite, TestCase } from '../../types/testReporter';
|
||||||
import { formatFailure, resolveOutputFile, stripAnsiEscapes } from './base';
|
import { formatFailure, resolveOutputFile, stripAnsiEscapes } from './base';
|
||||||
import EmptyReporter from './empty';
|
import EmptyReporter from './empty';
|
||||||
|
import { getAsBooleanFromENV } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
type JUnitOptions = {
|
type JUnitOptions = {
|
||||||
outputFile?: string,
|
outputFile?: string,
|
||||||
|
|
@ -42,8 +43,8 @@ class JUnitReporter extends EmptyReporter {
|
||||||
|
|
||||||
constructor(options: JUnitOptions) {
|
constructor(options: JUnitOptions) {
|
||||||
super();
|
super();
|
||||||
this.stripANSIControlSequences = options.stripANSIControlSequences || false;
|
this.stripANSIControlSequences = getAsBooleanFromENV('PLAYWRIGHT_JUNIT_STRIP_ANSI', !!options.stripANSIControlSequences);
|
||||||
this.includeProjectInTestName = options.includeProjectInTestName || false;
|
this.includeProjectInTestName = getAsBooleanFromENV('PLAYWRIGHT_JUNIT_INCLUDE_PROJECT_IN_TEST_NAME', !!options.includeProjectInTestName);
|
||||||
this.configDir = options.configDir;
|
this.configDir = options.configDir;
|
||||||
this.resolvedOutputFile = resolveOutputFile('JUNIT', options)?.outputFile;
|
this.resolvedOutputFile = resolveOutputFile('JUNIT', options)?.outputFile;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
||||||
import { colors, BaseReporter, formatError, formatTestTitle, isTTY, stepSuffix, stripAnsiEscapes, ttyWidth } from './base';
|
import { colors, BaseReporter, formatError, formatTestTitle, isTTY, stepSuffix, stripAnsiEscapes, ttyWidth } from './base';
|
||||||
import type { FullResult, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter';
|
import type { FullResult, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter';
|
||||||
|
import { getAsBooleanFromENV } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
// Allow it in the Visual Studio Code Terminal and the new Windows Terminal
|
// Allow it in the Visual Studio Code Terminal and the new Windows Terminal
|
||||||
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
||||||
|
|
@ -33,9 +34,9 @@ class ListReporter extends BaseReporter {
|
||||||
private _needNewLine = false;
|
private _needNewLine = false;
|
||||||
private _printSteps: boolean;
|
private _printSteps: boolean;
|
||||||
|
|
||||||
constructor(options: { omitFailures?: boolean, printSteps?: boolean } = {}) {
|
constructor(options: { printSteps?: boolean } = {}) {
|
||||||
super(options);
|
super();
|
||||||
this._printSteps = isTTY && (options.printSteps || !!process.env.PW_TEST_DEBUG_REPORTERS_PRINT_STEPS);
|
this._printSteps = isTTY && getAsBooleanFromENV('PLAYWRIGHT_LIST_PRINT_STEPS', options.printSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
override printsToStdio() {
|
override printsToStdio() {
|
||||||
|
|
@ -229,8 +230,6 @@ class ListReporter extends BaseReporter {
|
||||||
// Go down if needed.
|
// Go down if needed.
|
||||||
if (row !== this._lastRow)
|
if (row !== this._lastRow)
|
||||||
process.stdout.write(`\u001B[${this._lastRow - row}E`);
|
process.stdout.write(`\u001B[${this._lastRow - row}E`);
|
||||||
if (process.env.PWTEST_TTY_WIDTH)
|
|
||||||
process.stdout.write('\n'); // For testing.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _testPrefix(index: string, statusMark: string) {
|
private _testPrefix(index: string, statusMark: string) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,15 @@ export class FailureTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
result(): 'failed' | 'passed' {
|
result(): 'failed' | 'passed' {
|
||||||
return this._hasWorkerErrors || this.hasReachedMaxFailures() || this._rootSuite?.allTests().some(test => !test.ok()) ? 'failed' : 'passed';
|
return this._hasWorkerErrors || this.hasReachedMaxFailures() || this.hasFailedTests() || (this._config.cliFailOnFlakyTests && this.hasFlakyTests()) ? 'failed' : 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFailedTests() {
|
||||||
|
return this._rootSuite?.allTests().some(test => !test.ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFlakyTests() {
|
||||||
|
return this._rootSuite?.allTests().some(test => (test.outcome() === 'flaky'));
|
||||||
}
|
}
|
||||||
|
|
||||||
maxFailures() {
|
maxFailures() {
|
||||||
|
|
|
||||||
|
|
@ -308,10 +308,12 @@ class TestServerDispatcher implements TestServerInterface {
|
||||||
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
||||||
use: {
|
use: {
|
||||||
trace: params.trace === 'on' ? { mode: 'on', sources: false, _live: true } : (params.trace === 'off' ? 'off' : undefined),
|
trace: params.trace === 'on' ? { mode: 'on', sources: false, _live: true } : (params.trace === 'off' ? 'off' : undefined),
|
||||||
|
video: params.video === 'on' ? 'on' : (params.video === 'off' ? 'off' : undefined),
|
||||||
headless: params.headed ? false : undefined,
|
headless: params.headed ? false : undefined,
|
||||||
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
||||||
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
||||||
},
|
},
|
||||||
|
outputDir: params.outputDir,
|
||||||
workers: params.workers,
|
workers: params.workers,
|
||||||
};
|
};
|
||||||
if (params.trace === 'on')
|
if (params.trace === 'on')
|
||||||
|
|
@ -417,9 +419,9 @@ export async function runUIMode(configFile: string | undefined, options: TraceVi
|
||||||
return await innerRunTestServer(configLocation, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => {
|
return await innerRunTestServer(configLocation, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => {
|
||||||
await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' });
|
await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' });
|
||||||
if (options.host !== undefined || options.port !== undefined) {
|
if (options.host !== undefined || options.port !== undefined) {
|
||||||
await openTraceInBrowser(server.urlPrefix());
|
await openTraceInBrowser(server.urlPrefix('human-readable'));
|
||||||
} else {
|
} else {
|
||||||
const page = await openTraceViewerApp(server.urlPrefix(), 'chromium', {
|
const page = await openTraceViewerApp(server.urlPrefix('precise'), 'chromium', {
|
||||||
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||||
persistentContextOptions: {
|
persistentContextOptions: {
|
||||||
handleSIGINT: false,
|
handleSIGINT: false,
|
||||||
|
|
@ -434,7 +436,7 @@ export async function runTestServer(configFile: string | undefined, options: { h
|
||||||
const configLocation = resolveConfigLocation(configFile);
|
const configLocation = resolveConfigLocation(configFile);
|
||||||
return await innerRunTestServer(configLocation, options, async server => {
|
return await innerRunTestServer(configLocation, options, async server => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('Listening on ' + server.urlPrefix().replace('http:', 'ws:') + '/' + server.wsGuid());
|
console.log('Listening on ' + server.urlPrefix('precise').replace('http:', 'ws:') + '/' + server.wsGuid());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,12 +100,17 @@ export class WorkerMain extends ProcessRunner {
|
||||||
override async gracefullyClose() {
|
override async gracefullyClose() {
|
||||||
try {
|
try {
|
||||||
await this._stop();
|
await this._stop();
|
||||||
|
// Ignore top-level errors, they are already inside TestInfo.errors.
|
||||||
|
const fakeTestInfo = new TestInfoImpl(this._config, this._project, this._params, undefined, 0, () => {}, () => {}, () => {});
|
||||||
|
const runnable = { type: 'teardown' } as const;
|
||||||
// We have to load the project to get the right deadline below.
|
// We have to load the project to get the right deadline below.
|
||||||
await this._loadIfNeeded();
|
await fakeTestInfo._runAsStage({ title: 'worker cleanup', runnable }, () => this._loadIfNeeded()).catch(() => {});
|
||||||
await this._teardownScopes();
|
await this._fixtureRunner.teardownScope('test', fakeTestInfo, runnable).catch(() => {});
|
||||||
|
await this._fixtureRunner.teardownScope('worker', fakeTestInfo, runnable).catch(() => {});
|
||||||
// Close any other browsers launched in this process. This includes anything launched
|
// Close any other browsers launched in this process. This includes anything launched
|
||||||
// manually in the test/hooks and internal browsers like Playwright Inspector.
|
// manually in the test/hooks and internal browsers like Playwright Inspector.
|
||||||
await gracefullyCloseAll();
|
await fakeTestInfo._runAsStage({ title: 'worker cleanup', runnable }, () => gracefullyCloseAll()).catch(() => {});
|
||||||
|
this._fatalErrors.push(...fakeTestInfo.errors);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._fatalErrors.push(serializeError(e));
|
this._fatalErrors.push(serializeError(e));
|
||||||
}
|
}
|
||||||
|
|
@ -144,15 +149,6 @@ export class WorkerMain extends ProcessRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _teardownScopes() {
|
|
||||||
const fakeTestInfo = new TestInfoImpl(this._config, this._project, this._params, undefined, 0, () => {}, () => {}, () => {});
|
|
||||||
const runnable = { type: 'teardown' } as const;
|
|
||||||
// Ignore top-level errors, they are already inside TestInfo.errors.
|
|
||||||
await this._fixtureRunner.teardownScope('test', fakeTestInfo, runnable).catch(() => {});
|
|
||||||
await this._fixtureRunner.teardownScope('worker', fakeTestInfo, runnable).catch(() => {});
|
|
||||||
this._fatalErrors.push(...fakeTestInfo.errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
unhandledError(error: Error | any) {
|
unhandledError(error: Error | any) {
|
||||||
// No current test - fatal error.
|
// No current test - fatal error.
|
||||||
if (!this._currentTest) {
|
if (!this._currentTest) {
|
||||||
|
|
|
||||||
6
packages/playwright/types/test.d.ts
vendored
6
packages/playwright/types/test.d.ts
vendored
|
|
@ -2184,7 +2184,8 @@ interface TestFunction<TestArgs> {
|
||||||
* test('basic test', {
|
* test('basic test', {
|
||||||
* annotation: {
|
* annotation: {
|
||||||
* type: 'issue',
|
* type: 'issue',
|
||||||
* description: 'https://github.com/microsoft/playwright/issues/23180',
|
* description: 'feature tags API',
|
||||||
|
* url: 'https://github.com/microsoft/playwright/issues/23180'
|
||||||
* },
|
* },
|
||||||
* }, async ({ page }) => {
|
* }, async ({ page }) => {
|
||||||
* await page.goto('https://playwright.dev/');
|
* await page.goto('https://playwright.dev/');
|
||||||
|
|
@ -2260,7 +2261,8 @@ interface TestFunction<TestArgs> {
|
||||||
* test('basic test', {
|
* test('basic test', {
|
||||||
* annotation: {
|
* annotation: {
|
||||||
* type: 'issue',
|
* type: 'issue',
|
||||||
* description: 'https://github.com/microsoft/playwright/issues/23180',
|
* description: 'feature tags API',
|
||||||
|
* url: 'https://github.com/microsoft/playwright/issues/23180'
|
||||||
* },
|
* },
|
||||||
* }, async ({ page }) => {
|
* }, async ({ page }) => {
|
||||||
* await page.goto('https://playwright.dev/');
|
* await page.goto('https://playwright.dev/');
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,8 @@ function indexModel(context: ContextEntry) {
|
||||||
}
|
}
|
||||||
for (const event of context.events)
|
for (const event of context.events)
|
||||||
(event as any)[contextSymbol] = context;
|
(event as any)[contextSymbol] = context;
|
||||||
|
for (const resource of context.resources)
|
||||||
|
(resource as any)[contextSymbol] = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeActionsAndUpdateTiming(contexts: ContextEntry[]) {
|
function mergeActionsAndUpdateTiming(contexts: ContextEntry[]) {
|
||||||
|
|
@ -330,7 +332,7 @@ export function idForAction(action: ActionTraceEvent) {
|
||||||
return `${action.pageId || 'none'}:${action.callId}`;
|
return `${action.pageId || 'none'}:${action.callId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function context(action: ActionTraceEvent | trace.EventTraceEvent): ContextEntry {
|
export function context(action: ActionTraceEvent | trace.EventTraceEvent | ResourceSnapshot): ContextEntry {
|
||||||
return (action as any)[contextSymbol];
|
return (action as any)[contextSymbol];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@ import './networkTab.css';
|
||||||
import { NetworkResourceDetails } from './networkResourceDetails';
|
import { NetworkResourceDetails } from './networkResourceDetails';
|
||||||
import { bytesToString, msToString } from '@web/uiUtils';
|
import { bytesToString, msToString } from '@web/uiUtils';
|
||||||
import { PlaceholderPanel } from './placeholderPanel';
|
import { PlaceholderPanel } from './placeholderPanel';
|
||||||
import type { MultiTraceModel } from './modelUtil';
|
import { context, type MultiTraceModel } from './modelUtil';
|
||||||
import { GridView, type RenderedGridCell } from '@web/components/gridView';
|
import { GridView, type RenderedGridCell } from '@web/components/gridView';
|
||||||
import { SplitView } from '@web/components/splitView';
|
import { SplitView } from '@web/components/splitView';
|
||||||
|
import type { ContextEntry } from '../entries';
|
||||||
|
|
||||||
type NetworkTabModel = {
|
type NetworkTabModel = {
|
||||||
resources: Entry[],
|
resources: Entry[],
|
||||||
|
contextIdMap: ContextIdMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
type RenderedEntry = {
|
type RenderedEntry = {
|
||||||
|
|
@ -39,6 +41,7 @@ type RenderedEntry = {
|
||||||
start: number,
|
start: number,
|
||||||
route: string,
|
route: string,
|
||||||
resource: Entry,
|
resource: Entry,
|
||||||
|
contextId: string,
|
||||||
};
|
};
|
||||||
type ColumnName = keyof RenderedEntry;
|
type ColumnName = keyof RenderedEntry;
|
||||||
type Sorting = { by: ColumnName, negate: boolean};
|
type Sorting = { by: ColumnName, negate: boolean};
|
||||||
|
|
@ -54,7 +57,8 @@ export function useNetworkTabModel(model: MultiTraceModel | undefined, selectedT
|
||||||
});
|
});
|
||||||
return filtered;
|
return filtered;
|
||||||
}, [model, selectedTime]);
|
}, [model, selectedTime]);
|
||||||
return { resources };
|
const contextIdMap = React.useMemo(() => new ContextIdMap(model), [model]);
|
||||||
|
return { resources, contextIdMap };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetworkTab: React.FunctionComponent<{
|
export const NetworkTab: React.FunctionComponent<{
|
||||||
|
|
@ -66,11 +70,11 @@ export const NetworkTab: React.FunctionComponent<{
|
||||||
const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined);
|
const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined);
|
||||||
|
|
||||||
const { renderedEntries } = React.useMemo(() => {
|
const { renderedEntries } = React.useMemo(() => {
|
||||||
const renderedEntries = networkModel.resources.map(entry => renderEntry(entry, boundaries));
|
const renderedEntries = networkModel.resources.map(entry => renderEntry(entry, boundaries, networkModel.contextIdMap));
|
||||||
if (sorting)
|
if (sorting)
|
||||||
sort(renderedEntries, sorting);
|
sort(renderedEntries, sorting);
|
||||||
return { renderedEntries };
|
return { renderedEntries };
|
||||||
}, [networkModel.resources, sorting, boundaries]);
|
}, [networkModel.resources, networkModel.contextIdMap, sorting, boundaries]);
|
||||||
|
|
||||||
if (!networkModel.resources.length)
|
if (!networkModel.resources.length)
|
||||||
return <PlaceholderPanel text='No network calls' />;
|
return <PlaceholderPanel text='No network calls' />;
|
||||||
|
|
@ -81,7 +85,7 @@ export const NetworkTab: React.FunctionComponent<{
|
||||||
selectedItem={selectedEntry}
|
selectedItem={selectedEntry}
|
||||||
onSelected={item => setSelectedEntry(item)}
|
onSelected={item => setSelectedEntry(item)}
|
||||||
onHighlighted={item => onEntryHovered(item?.resource)}
|
onHighlighted={item => onEntryHovered(item?.resource)}
|
||||||
columns={selectedEntry ? ['name'] : ['name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route']}
|
columns={visibleColumns(!!selectedEntry, renderedEntries)}
|
||||||
columnTitle={columnTitle}
|
columnTitle={columnTitle}
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
isError={item => item.status.code >= 400}
|
isError={item => item.status.code >= 400}
|
||||||
|
|
@ -100,6 +104,8 @@ export const NetworkTab: React.FunctionComponent<{
|
||||||
};
|
};
|
||||||
|
|
||||||
const columnTitle = (column: ColumnName) => {
|
const columnTitle = (column: ColumnName) => {
|
||||||
|
if (column === 'contextId')
|
||||||
|
return 'Source';
|
||||||
if (column === 'name')
|
if (column === 'name')
|
||||||
return 'Name';
|
return 'Name';
|
||||||
if (column === 'method')
|
if (column === 'method')
|
||||||
|
|
@ -128,10 +134,28 @@ const columnWidth = (column: ColumnName) => {
|
||||||
return 60;
|
return 60;
|
||||||
if (column === 'contentType')
|
if (column === 'contentType')
|
||||||
return 200;
|
return 200;
|
||||||
|
if (column === 'contextId')
|
||||||
|
return 60;
|
||||||
return 100;
|
return 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function visibleColumns(entrySelected: boolean, renderedEntries: RenderedEntry[]): (keyof RenderedEntry)[] {
|
||||||
|
if (entrySelected)
|
||||||
|
return ['name'];
|
||||||
|
const columns: (keyof RenderedEntry)[] = [];
|
||||||
|
if (hasMultipleContexts(renderedEntries))
|
||||||
|
columns.push('contextId');
|
||||||
|
columns.push('name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route');
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => {
|
const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => {
|
||||||
|
if (column === 'contextId') {
|
||||||
|
return {
|
||||||
|
body: entry.contextId,
|
||||||
|
title: entry.name.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (column === 'name') {
|
if (column === 'name') {
|
||||||
return {
|
return {
|
||||||
body: entry.name.name,
|
body: entry.name.name,
|
||||||
|
|
@ -159,7 +183,57 @@ const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell
|
||||||
return { body: '' };
|
return { body: '' };
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => {
|
class ContextIdMap {
|
||||||
|
private _pagerefToShortId = new Map<string, string>();
|
||||||
|
private _contextToId = new Map<ContextEntry, string>();
|
||||||
|
private _lastPageId = 0;
|
||||||
|
private _lastApiRequestContextId = 0;
|
||||||
|
|
||||||
|
constructor(model: MultiTraceModel | undefined) {}
|
||||||
|
|
||||||
|
contextId(resource: Entry): string {
|
||||||
|
if (resource.pageref)
|
||||||
|
return this._pageId(resource.pageref);
|
||||||
|
else if (resource._apiRequest)
|
||||||
|
return this._apiRequestContextId(resource);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pageId(pageref: string): string {
|
||||||
|
let shortId = this._pagerefToShortId.get(pageref);
|
||||||
|
if (!shortId) {
|
||||||
|
++this._lastPageId;
|
||||||
|
shortId = 'page#' + this._lastPageId;
|
||||||
|
this._pagerefToShortId.set(pageref, shortId);
|
||||||
|
}
|
||||||
|
return shortId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _apiRequestContextId(resource: Entry): string {
|
||||||
|
const contextEntry = context(resource);
|
||||||
|
if (!contextEntry)
|
||||||
|
return '';
|
||||||
|
let contextId = this._contextToId.get(contextEntry);
|
||||||
|
if (!contextId) {
|
||||||
|
++this._lastApiRequestContextId;
|
||||||
|
contextId = 'api#' + this._lastApiRequestContextId;
|
||||||
|
this._contextToId.set(contextEntry, contextId);
|
||||||
|
}
|
||||||
|
return contextId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMultipleContexts(renderedEntries: RenderedEntry[]): boolean {
|
||||||
|
const contextIds = new Set<string>();
|
||||||
|
for (const entry of renderedEntries) {
|
||||||
|
contextIds.add(entry.contextId);
|
||||||
|
if (contextIds.size > 1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderEntry = (resource: Entry, boundaries: Boundaries, contextIdGenerator: ContextIdMap): RenderedEntry => {
|
||||||
const routeStatus = formatRouteStatus(resource);
|
const routeStatus = formatRouteStatus(resource);
|
||||||
let resourceName: string;
|
let resourceName: string;
|
||||||
try {
|
try {
|
||||||
|
|
@ -184,7 +258,8 @@ const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry =>
|
||||||
size: resource.response._transferSize! > 0 ? resource.response._transferSize! : resource.response.bodySize,
|
size: resource.response._transferSize! > 0 ? resource.response._transferSize! : resource.response.bodySize,
|
||||||
start: resource._monotonicTime! - boundaries.minimum,
|
start: resource._monotonicTime! - boundaries.minimum,
|
||||||
route: routeStatus,
|
route: routeStatus,
|
||||||
resource
|
resource,
|
||||||
|
contextId: contextIdGenerator.contextId(resource),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -249,4 +324,7 @@ function comparator(sortBy: ColumnName) {
|
||||||
return a.route.localeCompare(b.route);
|
return a.route.localeCompare(b.route);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sortBy === 'contextId')
|
||||||
|
return (a: RenderedEntry, b: RenderedEntry) => a.contextId.localeCompare(b.contextId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ const queryParams = {
|
||||||
workers: searchParams.get('workers') || undefined,
|
workers: searchParams.get('workers') || undefined,
|
||||||
timeout: searchParams.has('timeout') ? +searchParams.get('timeout')! : undefined,
|
timeout: searchParams.has('timeout') ? +searchParams.get('timeout')! : undefined,
|
||||||
headed: searchParams.has('headed'),
|
headed: searchParams.has('headed'),
|
||||||
|
outputDir: searchParams.get('outputDir') || undefined,
|
||||||
reporters: searchParams.has('reporter') ? searchParams.getAll('reporter') : undefined,
|
reporters: searchParams.has('reporter') ? searchParams.getAll('reporter') : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -283,6 +284,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
workers: queryParams.workers,
|
workers: queryParams.workers,
|
||||||
timeout: queryParams.timeout,
|
timeout: queryParams.timeout,
|
||||||
headed: queryParams.headed,
|
headed: queryParams.headed,
|
||||||
|
outputDir: queryParams.outputDir,
|
||||||
reporters: queryParams.reporters,
|
reporters: queryParams.reporters,
|
||||||
trace: 'on',
|
trace: 'on',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export function useMeasure<T extends Element>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function msToString(ms: number): string {
|
export function msToString(ms: number): string {
|
||||||
if (!isFinite(ms))
|
if (ms < 0 || !isFinite(ms))
|
||||||
return '-';
|
return '-';
|
||||||
|
|
||||||
if (ms === 0)
|
if (ms === 0)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { test } from './npmTest';
|
import { test } from './npmTest';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
test('electron should work', async ({ exec, tsc, writeFiles }) => {
|
test('electron should work', async ({ exec, tsc, writeFiles }) => {
|
||||||
await exec('npm i playwright electron@19.0.11');
|
await exec('npm i playwright electron@19.0.11');
|
||||||
|
|
@ -24,3 +26,16 @@ test('electron should work', async ({ exec, tsc, writeFiles }) => {
|
||||||
});
|
});
|
||||||
await tsc('test.ts');
|
await tsc('test.ts');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('electron should work with special characters in path', async ({ exec, tmpWorkspace }) => {
|
||||||
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30755' });
|
||||||
|
const folderName = path.join(tmpWorkspace, '!@#$% тест with spaces and 😊');
|
||||||
|
|
||||||
|
await exec('npm i playwright electron@19.0.11');
|
||||||
|
await fs.promises.mkdir(folderName);
|
||||||
|
for (const file of ['electron-app.js', 'sanity-electron.js'])
|
||||||
|
await fs.promises.copyFile(path.join(tmpWorkspace, file), path.join(folderName, file));
|
||||||
|
await exec('node sanity-electron.js', {
|
||||||
|
cwd: path.join(folderName)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,20 @@ it('should follow redirects', async ({ context, server }) => {
|
||||||
expect(await response.json()).toEqual({ foo: 'bar' });
|
expect(await response.json()).toEqual({ foo: 'bar' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should follow redirects correctly when Location header contains UTF-8 characters', async ({ context, server }) => {
|
||||||
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30903' });
|
||||||
|
server.setRoute('/redirect', (req, res) => {
|
||||||
|
// Node.js only allows US-ASCII, so we can't send invalid headers directly. Sending it as a raw response instead.
|
||||||
|
res.socket.write('HTTP/1.1 301 Moved Permanently\r\n');
|
||||||
|
res.socket.write(`Location: ${server.PREFIX}/empty.html?message=マスクПривет\r\n`);
|
||||||
|
res.socket.write('\r\n');
|
||||||
|
res.socket.uncork();
|
||||||
|
res.socket.end();
|
||||||
|
});
|
||||||
|
const response = await context.request.get(server.PREFIX + '/redirect');
|
||||||
|
expect(response.url()).toBe(server.PREFIX + '/empty.html?' + new URLSearchParams({ message: 'マスクПривет' }));
|
||||||
|
});
|
||||||
|
|
||||||
it('should add cookies from Set-Cookie header', async ({ context, page, server }) => {
|
it('should add cookies from Set-Cookie header', async ({ context, page, server }) => {
|
||||||
server.setRoute('/setcookie.html', (req, res) => {
|
server.setRoute('/setcookie.html', (req, res) => {
|
||||||
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
|
res.setHeader('Set-Cookie', ['session=value', 'foo=bar; max-age=3600']);
|
||||||
|
|
@ -794,21 +808,6 @@ it('should respect timeout after redirects', async function({ context, server })
|
||||||
expect(error.message).toContain(`Request timed out after 100ms`);
|
expect(error.message).toContain(`Request timed out after 100ms`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on a redirect with an invalid URL', async ({ context, server }) => {
|
|
||||||
server.setRedirect('/redirect', '/test');
|
|
||||||
server.setRoute('/test', (req, res) => {
|
|
||||||
// Node.js prevents us from responding with an invalid header, therefore we manually write the response.
|
|
||||||
const conn = res.connection!;
|
|
||||||
conn.write('HTTP/1.1 302\r\n');
|
|
||||||
conn.write('Location: https://здравствуйте/\r\n');
|
|
||||||
conn.write('\r\n');
|
|
||||||
conn.uncork();
|
|
||||||
conn.end();
|
|
||||||
});
|
|
||||||
const error = await context.request.get(server.PREFIX + '/redirect').catch(e => e);
|
|
||||||
expect(error.message).toContain('apiRequestContext.get: uri requested responds with an invalid redirect URL');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not hang on a brotli encoded Range request', async ({ context, server }) => {
|
it('should not hang on a brotli encoded Range request', async ({ context, server }) => {
|
||||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/18190' });
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/18190' });
|
||||||
it.skip(+process.versions.node.split('.')[0] < 18);
|
it.skip(+process.versions.node.split('.')[0] < 18);
|
||||||
|
|
@ -1256,3 +1255,8 @@ it('should not work after dispose', async ({ context, server }) => {
|
||||||
await context.request.dispose();
|
await context.request.dispose();
|
||||||
expect(await context.request.get(server.EMPTY_PAGE).catch(e => e.message)).toContain(kTargetClosedErrorMessage);
|
expect(await context.request.get(server.EMPTY_PAGE).catch(e => e.message)).toContain(kTargetClosedErrorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not work after context dispose', async ({ context, server }) => {
|
||||||
|
await context.close({ reason: 'Test ended.' });
|
||||||
|
expect(await context.request.get(server.EMPTY_PAGE).catch(e => e.message)).toContain('Test ended.');
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ it('SharedArrayBuffer should work @smoke', async function({ contextFactory, http
|
||||||
expect(await page.evaluate(() => typeof SharedArrayBuffer)).toBe('function');
|
expect(await page.evaluate(() => typeof SharedArrayBuffer)).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Web Assembly should work @smoke', async function({ page, server }) {
|
it('Web Assembly should work @smoke', async ({ page, server, browserName, platform }) => {
|
||||||
|
it.fixme(browserName === 'webkit' && platform === 'win32', 'Windows JIT is disabled: https://bugs.webkit.org/show_bug.cgi?id=273854');
|
||||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||||
expect(await page.evaluate('loadTable()')).toBe('42, 83');
|
expect(await page.evaluate('loadTable()')).toBe('42, 83');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,9 @@ import { test as it, expect } from './pageTest';
|
||||||
import { attachFrame } from '../config/utils';
|
import { attachFrame } from '../config/utils';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
function adjustServerHeaders(headers: Object, browserName: string, channel: string) {
|
function adjustServerHeaders(headers: Object, browserName: string) {
|
||||||
if (browserName === 'firefox' && channel === 'firefox-beta') {
|
if (browserName === 'firefox')
|
||||||
// This is a new experimental feature, only enabled in Firefox Beta for now.
|
|
||||||
delete headers['priority'];
|
delete headers['priority'];
|
||||||
}
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +88,7 @@ it('should return headers', async ({ page, server, browserName }) => {
|
||||||
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => {
|
it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => {
|
||||||
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
||||||
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
||||||
let serverRequest;
|
let serverRequest;
|
||||||
|
|
@ -100,10 +98,10 @@ it('should get the same headers as the server', async ({ page, server, browserNa
|
||||||
});
|
});
|
||||||
const response = await page.goto(server.PREFIX + '/empty.html');
|
const response = await page.goto(server.PREFIX + '/empty.html');
|
||||||
const headers = await response.request().allHeaders();
|
const headers = await response.request().allHeaders();
|
||||||
expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel));
|
expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => {
|
it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => {
|
||||||
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
||||||
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
||||||
|
|
||||||
|
|
@ -122,13 +120,13 @@ it('should not return allHeaders() until they are available', async ({ page, ser
|
||||||
|
|
||||||
await page.goto(server.PREFIX + '/empty.html');
|
await page.goto(server.PREFIX + '/empty.html');
|
||||||
const requestHeaders = await requestHeadersPromise;
|
const requestHeaders = await requestHeadersPromise;
|
||||||
expect(requestHeaders).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel));
|
expect(requestHeaders).toEqual(adjustServerHeaders(serverRequest.headers, browserName));
|
||||||
|
|
||||||
const responseHeaders = await responseHeadersPromise;
|
const responseHeaders = await responseHeadersPromise;
|
||||||
expect(responseHeaders['foo']).toBe('bar');
|
expect(responseHeaders['foo']).toBe('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => {
|
it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, }) => {
|
||||||
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99');
|
||||||
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language');
|
||||||
|
|
||||||
|
|
@ -147,7 +145,7 @@ it('should get the same headers as the server CORS', async ({ page, server, brow
|
||||||
expect(text).toBe('done');
|
expect(text).toBe('done');
|
||||||
const response = await responsePromise;
|
const response = await responsePromise;
|
||||||
const headers = await response.request().allHeaders();
|
const headers = await response.request().allHeaders();
|
||||||
expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel));
|
expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not get preflight CORS requests when intercepting', async ({ page, server, browserName, isAndroid }) => {
|
it('should not get preflight CORS requests when intercepting', async ({ page, server, browserName, isAndroid }) => {
|
||||||
|
|
@ -408,10 +406,9 @@ it('should report raw headers', async ({ page, server, browserName, platform, is
|
||||||
return { name, value: values[0] };
|
return { name, value: values[0] };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (browserName === 'firefox' && channel === 'firefox-beta') {
|
if (browserName === 'firefox')
|
||||||
// This is a new experimental feature, only enabled in Firefox Beta for now.
|
|
||||||
expectedHeaders = expectedHeaders.filter(({ name }) => name.toLowerCase() !== 'priority');
|
expectedHeaders = expectedHeaders.filter(({ name }) => name.toLowerCase() !== 'priority');
|
||||||
}
|
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
|
||||||
|
|
@ -477,3 +477,46 @@ it('should intercept css variable with background url', async ({ page, server })
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
expect(interceptedRequests).toBe(1);
|
expect(interceptedRequests).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('continue should not change multipart/form-data body', async ({ page, server, browserName }) => {
|
||||||
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/19158' });
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
server.setRoute('/upload', (request, response) => {
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end('done');
|
||||||
|
});
|
||||||
|
async function sendFormData() {
|
||||||
|
const reqPromise = server.waitForRequest('/upload');
|
||||||
|
const status = await page.evaluate(async () => {
|
||||||
|
const newFile = new File(['file content'], 'file.txt');
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', newFile);
|
||||||
|
const response = await fetch('/upload', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
return response.status;
|
||||||
|
});
|
||||||
|
const req = await reqPromise;
|
||||||
|
expect(status).toBe(200);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
const reqBefore = await sendFormData();
|
||||||
|
await page.route('**/*', async route => {
|
||||||
|
await route.continue();
|
||||||
|
});
|
||||||
|
const reqAfter = await sendFormData();
|
||||||
|
const fileContent = [
|
||||||
|
'Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\"',
|
||||||
|
'Content-Type: application/octet-stream',
|
||||||
|
'',
|
||||||
|
'file content',
|
||||||
|
'------'].join('\r\n');
|
||||||
|
expect.soft((await reqBefore.postBody).toString('utf8')).toContain(fileContent);
|
||||||
|
expect.soft((await reqAfter.postBody).toString('utf8')).toContain(fileContent);
|
||||||
|
// Firefox sends a bit longer boundary.
|
||||||
|
const expectedLength = browserName === 'firefox' ? '246' : '208';
|
||||||
|
expect.soft(reqBefore.headers['content-length']).toBe(expectedLength);
|
||||||
|
expect.soft(reqAfter.headers['content-length']).toBe(expectedLength);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,9 @@ it('should work with status code 422', async ({ page, server }) => {
|
||||||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw exception if status code is not supported', async ({ page, server, browserName }) => {
|
it('should fulfill with unuassigned status codes', async ({ page, server, browserName }) => {
|
||||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28490' });
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28490' });
|
||||||
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30773' });
|
||||||
let fulfillPromiseCallback;
|
let fulfillPromiseCallback;
|
||||||
const fulfillPromise = new Promise<Error|undefined>(f => fulfillPromiseCallback = f);
|
const fulfillPromise = new Promise<Error|undefined>(f => fulfillPromiseCallback = f);
|
||||||
await page.route('**/data.json', route => {
|
await page.route('**/data.json', route => {
|
||||||
|
|
@ -89,14 +90,14 @@ it('should throw exception if status code is not supported', async ({ page, serv
|
||||||
}).catch(e => e));
|
}).catch(e => e));
|
||||||
});
|
});
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
page.evaluate(url => fetch(url), server.PREFIX + '/data.json').catch(() => {});
|
const response = await page.evaluate(async url => {
|
||||||
|
const { status, statusText } = await fetch(url);
|
||||||
|
return { status, statusText };
|
||||||
|
}, server.PREFIX + '/data.json');
|
||||||
const error = await fulfillPromise;
|
const error = await fulfillPromise;
|
||||||
if (browserName === 'chromium') {
|
expect(error).toBe(undefined);
|
||||||
expect(error).toBeTruthy();
|
expect(response.status).toBe(430);
|
||||||
expect(error.message).toContain(' Invalid http status code or phrase');
|
expect(response.statusText).toBe('Unknown');
|
||||||
} else {
|
|
||||||
expect(error).toBe(undefined);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if request was cancelled by the page', async ({ page, server }) => {
|
it('should not throw if request was cancelled by the page', async ({ page, server }) => {
|
||||||
|
|
@ -456,3 +457,30 @@ it('should fulfill with gzip and readback', {
|
||||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||||
expect(await response.text()).toContain(`<div>hello, world!</div>`);
|
expect(await response.text()).toContain(`<div>hello, world!</div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not go to the network for fulfilled requests body', {
|
||||||
|
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30760' },
|
||||||
|
}, async ({ page, server, browserName }) => {
|
||||||
|
await page.route('**/one-style.css', async route => {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 404,
|
||||||
|
contentType: 'text/plain',
|
||||||
|
body: 'Not Found! (mocked)',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let serverHit = false;
|
||||||
|
server.setRoute('/one-style.css', (req, res) => {
|
||||||
|
serverHit = true;
|
||||||
|
res.setHeader('Content-Type', 'text/css');
|
||||||
|
res.end('body { background-color: green; }');
|
||||||
|
});
|
||||||
|
|
||||||
|
const responsePromise = page.waitForResponse('**/one-style.css');
|
||||||
|
await page.goto(server.PREFIX + '/one-style.html');
|
||||||
|
const response = await responsePromise;
|
||||||
|
const body = await response.body();
|
||||||
|
expect(body).toBeTruthy();
|
||||||
|
expect(serverHit).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,19 @@ test('should allow flaky', async ({ runInlineTest }) => {
|
||||||
expect(result.flaky).toBe(1);
|
expect(result.flaky).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('failOnFlakyTests flag disallows flaky', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.js': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('flake', async ({}, testInfo) => {
|
||||||
|
expect(testInfo.retry).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}, { 'retries': 1, 'fail-on-flaky-tests': true });
|
||||||
|
expect(result.exitCode).not.toBe(0);
|
||||||
|
expect(result.flaky).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('should fail on unexpected pass', async ({ runInlineTest }) => {
|
test('should fail on unexpected pass', async ({ runInlineTest }) => {
|
||||||
const { exitCode, failed, output } = await runInlineTest({
|
const { exitCode, failed, output } = await runInlineTest({
|
||||||
'unexpected-pass.spec.js': `
|
'unexpected-pass.spec.js': `
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,7 @@ export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||||
GITHUB_SHA: undefined,
|
GITHUB_SHA: undefined,
|
||||||
// END: Reserved CI
|
// END: Reserved CI
|
||||||
PW_TEST_HTML_REPORT_OPEN: undefined,
|
PW_TEST_HTML_REPORT_OPEN: undefined,
|
||||||
|
PLAYWRIGHT_HTML_OPEN: undefined,
|
||||||
PW_TEST_REPORTER: undefined,
|
PW_TEST_REPORTER: undefined,
|
||||||
PW_TEST_REPORTER_WS_ENDPOINT: undefined,
|
PW_TEST_REPORTER_WS_ENDPOINT: undefined,
|
||||||
PW_TEST_SOURCE_TRANSFORM: undefined,
|
PW_TEST_SOURCE_TRANSFORM: undefined,
|
||||||
|
|
|
||||||
|
|
@ -85,35 +85,6 @@ test('should not reuse context with video if mode=when-possible', async ({ runIn
|
||||||
expect(fs.existsSync(testInfo.outputPath('test-results', 'reuse-two', 'video.webm'))).toBeFalsy();
|
expect(fs.existsSync(testInfo.outputPath('test-results', 'reuse-two', 'video.webm'))).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should reuse context and disable video if mode=force', async ({ runInlineTest }, testInfo) => {
|
|
||||||
const result = await runInlineTest({
|
|
||||||
'playwright.config.ts': `
|
|
||||||
export default {
|
|
||||||
use: { video: 'on' },
|
|
||||||
};
|
|
||||||
`,
|
|
||||||
'reuse.test.ts': `
|
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
let lastContextGuid;
|
|
||||||
|
|
||||||
test('one', async ({ context, page }) => {
|
|
||||||
lastContextGuid = context._guid;
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('two', async ({ context, page }) => {
|
|
||||||
expect(context._guid).toBe(lastContextGuid);
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
}, { workers: 1 }, { PW_TEST_REUSE_CONTEXT: '1' });
|
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
|
||||||
expect(result.passed).toBe(2);
|
|
||||||
expect(fs.existsSync(testInfo.outputPath('test-results', 'reuse-one', 'video.webm'))).toBeFalsy();
|
|
||||||
expect(fs.existsSync(testInfo.outputPath('test-results', 'reuse-two', 'video.webm'))).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should reuse context with trace if mode=when-possible', async ({ runInlineTest }, testInfo) => {
|
test('should reuse context with trace if mode=when-possible', async ({ runInlineTest }, testInfo) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ const test = baseTest.extend<{
|
||||||
await use(async (reportFolder?: string) => {
|
await use(async (reportFolder?: string) => {
|
||||||
reportFolder ??= test.info().outputPath('playwright-report');
|
reportFolder ??= test.info().outputPath('playwright-report');
|
||||||
server = startHtmlReportServer(reportFolder) as HttpServer;
|
server = startHtmlReportServer(reportFolder) as HttpServer;
|
||||||
const location = await server.start();
|
await server.start();
|
||||||
await page.goto(location);
|
await page.goto(server.urlPrefix('precise'));
|
||||||
});
|
});
|
||||||
await server?.stop();
|
await server?.stop();
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +209,7 @@ test('should merge into html with dependencies', async ({ runInlineTest, mergeRe
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
expect(output).not.toContain('To open last HTML report run:');
|
expect(output).not.toContain('To open last HTML report run:');
|
||||||
|
|
@ -280,7 +280,7 @@ test('should merge blob into blob', async ({ runInlineTest, mergeReports, showRe
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const compinedBlobReportDir = test.info().outputPath('blob-report');
|
const compinedBlobReportDir = test.info().outputPath('blob-report');
|
||||||
const { exitCode } = await mergeReports(compinedBlobReportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,json'] });
|
const { exitCode } = await mergeReports(compinedBlobReportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,json'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
expect(fs.existsSync(test.info().outputPath('report.json'))).toBe(true);
|
expect(fs.existsSync(test.info().outputPath('report.json'))).toBe(true);
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -335,7 +335,7 @@ test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports,
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -374,7 +374,7 @@ test('total time is from test run not from merge', async ({ runInlineTest, merge
|
||||||
await runInlineTest(files, { shard: `1/2` });
|
await runInlineTest(files, { shard: `1/2` });
|
||||||
await runInlineTest(files, { shard: `2/2` }, { PWTEST_BLOB_DO_NOT_REMOVE: '1' });
|
await runInlineTest(files, { shard: `2/2` }, { PWTEST_BLOB_DO_NOT_REMOVE: '1' });
|
||||||
|
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
expect(output).not.toContain('To open last HTML report run:');
|
expect(output).not.toContain('To open last HTML report run:');
|
||||||
|
|
@ -441,7 +441,7 @@ test('merge into list report by default', async ({ runInlineTest, mergeReports }
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip', 'report-3.zip']);
|
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip', 'report-3.zip']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' }, { additionalArgs: ['--reporter', 'list'] });
|
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_LIST_PRINT_STEPS: '1', PLAYWRIGHT_FORCE_TTY: '80' }, { additionalArgs: ['--reporter', 'list'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
const text = stripAnsi(output);
|
const text = stripAnsi(output);
|
||||||
|
|
@ -520,7 +520,7 @@ test('should print progress', async ({ runInlineTest, mergeReports }) => {
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
|
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_HTML_REPORT_OPEN: 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode, output } = await mergeReports(reportDir, { PLAYWRIGHT_HTML_OPEN: 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
const lines = output.split('\n');
|
const lines = output.split('\n');
|
||||||
|
|
@ -571,7 +571,7 @@ test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, p
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip']);
|
expect(reportFiles).toEqual(['report-1.zip']);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -634,7 +634,7 @@ test('generate html with attachment urls', async ({ runInlineTest, mergeReports,
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip']);
|
expect(reportFiles).toEqual(['report-1.zip']);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
const htmlReportDir = test.info().outputPath('playwright-report');
|
const htmlReportDir = test.info().outputPath('playwright-report');
|
||||||
|
|
@ -709,7 +709,7 @@ test('resource names should not clash between runs', async ({ runInlineTest, sho
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
|
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
|
||||||
|
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -781,7 +781,7 @@ test('multiple output reports', async ({ runInlineTest, mergeReports, showReport
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual(['report-1.zip']);
|
expect(reportFiles).toEqual(['report-1.zip']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,line'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,line'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
// Check that line reporter was called.
|
// Check that line reporter was called.
|
||||||
|
|
@ -907,7 +907,7 @@ test('onError in the report', async ({ runInlineTest, mergeReports, showReport,
|
||||||
const result = await runInlineTest(files, { shard: `1/3` }, { PWTEST_BOT_NAME: 'macos-node16-ttest' });
|
const result = await runInlineTest(files, { shard: `1/3` }, { PWTEST_BOT_NAME: 'macos-node16-ttest' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
|
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -1149,7 +1149,7 @@ test('preserve steps in html report', async ({ runInlineTest, mergeReports, show
|
||||||
// relative to the current directory.
|
// relative to the current directory.
|
||||||
const mergeCwd = test.info().outputPath('foo');
|
const mergeCwd = test.info().outputPath('foo');
|
||||||
await fs.promises.mkdir(mergeCwd, { recursive: true });
|
await fs.promises.mkdir(mergeCwd, { recursive: true });
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'], cwd: mergeCwd });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'], cwd: mergeCwd });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
expect(output).not.toContain('To open last HTML report run:');
|
expect(output).not.toContain('To open last HTML report run:');
|
||||||
|
|
@ -1326,7 +1326,7 @@ test('keep projects with same name different bot name separate', async ({ runInl
|
||||||
await runInlineTest(files('second'), undefined, { PWTEST_BOT_NAME: 'second', PWTEST_BLOB_DO_NOT_REMOVE: '1' });
|
await runInlineTest(files('second'), undefined, { PWTEST_BOT_NAME: 'second', PWTEST_BLOB_DO_NOT_REMOVE: '1' });
|
||||||
|
|
||||||
const reportDir = test.info().outputPath('blob-report');
|
const reportDir = test.info().outputPath('blob-report');
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('1');
|
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('1');
|
||||||
|
|
@ -1452,7 +1452,7 @@ test('merge-reports should throw if report version is from the future', async ({
|
||||||
await fs.promises.rm(reportZipFile, { force: true });
|
await fs.promises.rm(reportZipFile, { force: true });
|
||||||
await zipReport(events, reportZipFile);
|
await zipReport(events, reportZipFile);
|
||||||
|
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(1);
|
expect(exitCode).toBe(1);
|
||||||
expect(output).toContain(`Error: Blob report report-2.zip was created with a newer version of Playwright.`);
|
expect(output).toContain(`Error: Blob report report-2.zip was created with a newer version of Playwright.`);
|
||||||
|
|
||||||
|
|
@ -1496,7 +1496,7 @@ test('should merge blob reports with same name', async ({ runInlineTest, mergeRe
|
||||||
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-1.zip'));
|
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-1.zip'));
|
||||||
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-2.zip'));
|
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-2.zip'));
|
||||||
|
|
||||||
const { exitCode } = await mergeReports(allReportsDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(allReportsDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('10');
|
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('10');
|
||||||
|
|
@ -1887,7 +1887,7 @@ test('preserve static annotations when tests did not run', async ({ runInlineTes
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
await runInlineTest(files);
|
await runInlineTest(files);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ const test = baseTest.extend<{ showReport: (reportFolder?: string) => Promise<vo
|
||||||
await use(async (reportFolder?: string) => {
|
await use(async (reportFolder?: string) => {
|
||||||
reportFolder ??= testInfo.outputPath('playwright-report');
|
reportFolder ??= testInfo.outputPath('playwright-report');
|
||||||
server = startHtmlReportServer(reportFolder) as HttpServer;
|
server = startHtmlReportServer(reportFolder) as HttpServer;
|
||||||
const location = await server.start();
|
await server.start();
|
||||||
await page.goto(location);
|
await page.goto(server.urlPrefix('precise'));
|
||||||
});
|
});
|
||||||
await server?.stop();
|
await server?.stop();
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(testInfo.retry).toBe(1);
|
expect(testInfo.retry).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html', retries: 1 }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html', retries: 1 }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -95,6 +95,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(1).toBe(1);
|
await expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
|
// Note: using PW_TEST_HTML_REPORT_OPEN to test backwards compatibility.
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -113,7 +114,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(page.locator('.attachment-body')).toHaveText(/TESTID=.*/);
|
await expect(page.locator('.attachment-body')).toHaveText(/TESTID=.*/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not throw when PW_TEST_HTML_REPORT_OPEN value is invalid', async ({ runInlineTest, page, showReport }, testInfo) => {
|
test('should not throw when PLAYWRIGHT_HTML_OPEN value is invalid', async ({ runInlineTest, page, showReport }, testInfo) => {
|
||||||
const invalidOption = 'invalid-option';
|
const invalidOption = 'invalid-option';
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
|
|
@ -125,7 +126,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(2).toEqual(2);
|
expect(2).toEqual(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: invalidOption });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: invalidOption });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
@ -143,7 +144,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
testInfo.attachments.push({ name: 'screenshot', path: screenshot, contentType: 'image/png' });
|
testInfo.attachments.push({ name: 'screenshot', path: screenshot, contentType: 'image/png' });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -169,7 +170,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(screenshot).toMatchSnapshot('expected.png');
|
await expect(screenshot).toMatchSnapshot('expected.png');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -243,7 +244,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
|
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -278,7 +279,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect.soft(screenshot).toMatchSnapshot('expected.png');
|
await expect.soft(screenshot).toMatchSnapshot('expected.png');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -309,7 +310,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
|
await expect.soft(page).toHaveScreenshot({ timeout: 1000 });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { 'reporter': 'dot,html', 'update-snapshots': true }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { 'reporter': 'dot,html', 'update-snapshots': true }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -344,7 +345,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(screenshot).toMatchSnapshot('expected');
|
await expect(screenshot).toMatchSnapshot('expected');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -373,7 +374,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(true).toBeFalsy();
|
await expect(true).toBeFalsy();
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -404,7 +405,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await page.evaluate('2 + 2');
|
await page.evaluate('2 + 2');
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, {}, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, {}, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -432,7 +433,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(true).toBeFalsy();
|
await expect(true).toBeFalsy();
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -452,7 +453,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await expect(true).toBeFalsy();
|
await expect(true).toBeFalsy();
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.failed).toBe(1);
|
expect(result.failed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -475,7 +476,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await evaluateWrapper(page, '2 + 2');
|
await evaluateWrapper(page, '2 + 2');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -508,7 +509,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await page.evaluate('2 + 2');
|
await page.evaluate('2 + 2');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -538,7 +539,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await page.evaluate('2 + 2');
|
await page.evaluate('2 + 2');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -562,7 +563,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -600,7 +601,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -625,7 +626,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await page.evaluate('2 + 2');
|
await page.evaluate('2 + 2');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -683,7 +684,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
|
|
||||||
|
|
@ -726,7 +727,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test.info().annotations.push({ type: 'issue', description: 'I am not interested in this test' });
|
test.info().annotations.push({ type: 'issue', description: 'I am not interested in this test' });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -746,7 +747,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test.info().annotations.push({ type: 'issue', description: '${server.EMPTY_PAGE}' });
|
test.info().annotations.push({ type: 'issue', description: '${server.EMPTY_PAGE}' });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
|
@ -789,7 +790,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
@ -814,7 +815,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await testInfo.attach('example.ext with spaces', { body: Buffer.from('b'), contentType: 'madeup' });
|
await testInfo.attach('example.ext with spaces', { body: Buffer.from('b'), contentType: 'madeup' });
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
await page.getByRole('link', { name: 'passing' }).click();
|
await page.getByRole('link', { name: 'passing' }).click();
|
||||||
|
|
@ -867,7 +868,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect('new').toMatchSnapshot('snapshot.txt');
|
expect('new').toMatchSnapshot('snapshot.txt');
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
await showReport();
|
await showReport();
|
||||||
await page.click('text="is a test"');
|
await page.click('text="is a test"');
|
||||||
|
|
@ -894,7 +895,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect('newcommon').toMatchSnapshot('snapshot.txt');
|
expect('newcommon').toMatchSnapshot('snapshot.txt');
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
await showReport();
|
await showReport();
|
||||||
await page.click('text="is a test"');
|
await page.click('text="is a test"');
|
||||||
|
|
@ -912,7 +913,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
throw new Error('ouch');
|
throw new Error('ouch');
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'dot,html', 'repeat-each': 3 }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { 'reporter': 'dot,html', 'repeat-each': 3 }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -937,7 +938,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(2).toEqual(2);
|
expect(2).toEqual(2);
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { 'reporter': 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -956,7 +957,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('sample', async ({}) => { expect(2).toBe(2); });
|
test('sample', async ({}) => { expect(2).toBe(2); });
|
||||||
`,
|
`,
|
||||||
'a.spec.js': `require('./inner')`
|
'a.spec.js': `require('./inner')`
|
||||||
}, { 'reporter': 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { 'reporter': 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
await showReport();
|
await showReport();
|
||||||
await expect(page.locator('text=a.spec.js')).toBeVisible();
|
await expect(page.locator('text=a.spec.js')).toBeVisible();
|
||||||
|
|
@ -997,7 +998,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
await execGit(['commit', '-m', 'awesome commit message']);
|
await execGit(['commit', '-m', 'awesome commit message']);
|
||||||
|
|
||||||
const result = await runInlineTest(files, { reporter: 'dot,html' }, {
|
const result = await runInlineTest(files, { reporter: 'dot,html' }, {
|
||||||
PW_TEST_HTML_REPORT_OPEN: 'never',
|
PLAYWRIGHT_HTML_OPEN: 'never',
|
||||||
GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test',
|
GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test',
|
||||||
GITHUB_RUN_ID: 'example-run-id',
|
GITHUB_RUN_ID: 'example-run-id',
|
||||||
GITHUB_SERVER_URL: 'https://playwright.dev',
|
GITHUB_SERVER_URL: 'https://playwright.dev',
|
||||||
|
|
@ -1043,7 +1044,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('sample', async ({}) => { expect(2).toBe(2); });
|
test('sample', async ({}) => { expect(2).toBe(2); });
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never', GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test', GITHUB_RUN_ID: 'example-run-id', GITHUB_SERVER_URL: 'https://playwright.dev', GITHUB_SHA: 'example-sha' }, undefined);
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never', GITHUB_REPOSITORY: 'microsoft/playwright-example-for-test', GITHUB_RUN_ID: 'example-run-id', GITHUB_SERVER_URL: 'https://playwright.dev', GITHUB_SHA: 'example-sha' }, undefined);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -1071,7 +1072,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('my sample test', async ({}) => { expect(2).toBe(2); });
|
test('my sample test', async ({}) => { expect(2).toBe(2); });
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' }, undefined);
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }, undefined);
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -1095,7 +1096,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('my sample test', async ({}) => { expect(2).toBe(2); });
|
test('my sample test', async ({}) => { expect(2).toBe(2); });
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -1177,7 +1178,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('pass', ({}, testInfo) => {
|
test('pass', ({}, testInfo) => {
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'html,line' }, { PW_TEST_HTML_REPORT_OPEN: 'never' }, {
|
}, { 'reporter': 'html,line' }, { PLAYWRIGHT_HTML_OPEN: 'never' }, {
|
||||||
cwd: 'foo/bar/baz/tests',
|
cwd: 'foo/bar/baz/tests',
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|
@ -1201,7 +1202,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('pass', ({}, testInfo) => {
|
test('pass', ({}, testInfo) => {
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'html,line' }, { 'PW_TEST_HTML_REPORT_OPEN': 'never', 'PLAYWRIGHT_HTML_REPORT': '../my-report' }, {
|
}, { 'reporter': 'html,line' }, { 'PLAYWRIGHT_HTML_OPEN': 'never', 'PLAYWRIGHT_HTML_OUTPUT_DIR': '../my-report' }, {
|
||||||
cwd: 'foo/bar/baz/tests',
|
cwd: 'foo/bar/baz/tests',
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|
@ -1250,7 +1251,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(2);
|
expect(1).toBe(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -1324,7 +1325,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -1366,7 +1367,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -1407,7 +1408,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -1445,7 +1446,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(2);
|
expect(result.passed).toBe(2);
|
||||||
|
|
@ -1523,7 +1524,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(5);
|
expect(result.passed).toBe(5);
|
||||||
|
|
@ -1566,7 +1567,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(2);
|
expect(1).toBe(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -1615,7 +1616,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(7);
|
expect(result.passed).toBe(7);
|
||||||
|
|
@ -1689,7 +1690,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(2);
|
expect(1).toBe(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(2);
|
expect(result.passed).toBe(2);
|
||||||
|
|
@ -1755,7 +1756,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(2);
|
expect(1).toBe(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(2);
|
expect(result.passed).toBe(2);
|
||||||
|
|
@ -1804,7 +1805,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(2);
|
expect(1).toBe(2);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -1882,7 +1883,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -2021,7 +2022,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(0);
|
expect(1).toBe(0);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(3);
|
||||||
|
|
@ -2110,7 +2111,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('passes', () => {});
|
test('passes', () => {});
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -2139,7 +2140,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
test('test 6', async ({}) => {});
|
test('test 6', async ({}) => {});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
await showReport();
|
await showReport();
|
||||||
|
|
||||||
|
|
@ -2167,7 +2168,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('b test 1', async ({}) => {});
|
test('b test 1', async ({}) => {});
|
||||||
test('b test 2', async ({}) => {});
|
test('b test 2', async ({}) => {});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(4);
|
expect(result.passed).toBe(4);
|
||||||
|
|
@ -2197,7 +2198,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('failed title', async ({}) => { expect(1).toBe(1); });
|
test('failed title', async ({}) => { expect(1).toBe(1); });
|
||||||
test('passes title', async ({}) => { expect(1).toBe(2); });
|
test('passes title', async ({}) => { expect(1).toBe(2); });
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2220,7 +2221,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
test('test1', async ({}) => { expect(1).toBe(1); });
|
test('test1', async ({}) => { expect(1).toBe(1); });
|
||||||
test('test2', async ({}) => { expect(1).toBe(2); });
|
test('test2', async ({}) => { expect(1).toBe(2); });
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2257,7 +2258,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2286,7 +2287,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2315,7 +2316,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2344,7 +2345,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
expect(1).toBe(1);
|
expect(1).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2374,7 +2375,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
export default { globalTeardown: './globalTeardown.ts' };
|
export default { globalTeardown: './globalTeardown.ts' };
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
@ -2395,7 +2396,7 @@ for (const useIntermediateMergeReport of [false] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
|
||||||
|
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ test.describe('report location', () => {
|
||||||
test('pass', ({}, testInfo) => {
|
test('pass', ({}, testInfo) => {
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'json' }, { 'PW_TEST_HTML_REPORT_OPEN': 'never', 'PLAYWRIGHT_JSON_OUTPUT_NAME': '../my-report.json' }, {
|
}, { 'reporter': 'json' }, { 'PLAYWRIGHT_JSON_OUTPUT_NAME': '../my-report.json' }, {
|
||||||
cwd: 'foo/bar/baz/tests',
|
cwd: 'foo/bar/baz/tests',
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|
@ -302,7 +302,7 @@ test.describe('report location', () => {
|
||||||
test('pass', ({}, testInfo) => {
|
test('pass', ({}, testInfo) => {
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
}, { 'reporter': 'json' }, { 'PW_TEST_HTML_REPORT_OPEN': 'never', 'PLAYWRIGHT_JSON_OUTPUT_FILE': '../my-report.json' }, {
|
}, { 'reporter': 'json' }, { 'PLAYWRIGHT_JSON_OUTPUT_FILE': '../my-report.json' }, {
|
||||||
cwd: 'foo/bar/baz/tests',
|
cwd: 'foo/bar/baz/tests',
|
||||||
});
|
});
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||||
|
|
||||||
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
||||||
const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓ ';
|
const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓ ';
|
||||||
|
|
@ -70,7 +70,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' });
|
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_LIST_PRINT_STEPS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
|
||||||
const text = result.output;
|
const text = result.output;
|
||||||
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
|
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
|
||||||
lines.pop(); // Remove last item that contains [v] and time in ms.
|
lines.pop(); // Remove last item that contains [v] and time in ms.
|
||||||
|
|
@ -105,7 +105,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
await test.step('inner 2.2', async () => {});
|
await test.step('inner 2.2', async () => {});
|
||||||
});
|
});
|
||||||
});`,
|
});`,
|
||||||
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PWTEST_TTY_WIDTH: '80' });
|
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
|
||||||
const text = result.output;
|
const text = result.output;
|
||||||
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
|
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
|
||||||
lines.pop(); // Remove last item that contains [v] and time in ms.
|
lines.pop(); // Remove last item that contains [v] and time in ms.
|
||||||
|
|
@ -135,7 +135,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
console.log('a'.repeat(80) + 'b'.repeat(20));
|
console.log('a'.repeat(80) + 'b'.repeat(20));
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'list' }, { PWTEST_TTY_WIDTH: TTY_WIDTH + '' });
|
}, { reporter: 'list' }, { PLAYWRIGHT_FORCE_TTY: TTY_WIDTH + '' });
|
||||||
|
|
||||||
const renderedText = simpleAnsiRenderer(result.rawOutput, TTY_WIDTH);
|
const renderedText = simpleAnsiRenderer(result.rawOutput, TTY_WIDTH);
|
||||||
if (process.platform === 'win32')
|
if (process.platform === 'win32')
|
||||||
|
|
@ -154,7 +154,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
expect(testInfo.retry).toBe(1);
|
expect(testInfo.retry).toBe(1);
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'list', retries: '1' }, { PW_TEST_DEBUG_REPORTERS: '1', PWTEST_TTY_WIDTH: '80' });
|
}, { reporter: 'list', retries: '1' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
|
||||||
const text = result.output;
|
const text = result.output;
|
||||||
const lines = text.split('\n').filter(l => l.startsWith('0 :') || l.startsWith('1 :')).map(l => l.replace(/\d+(\.\d+)?m?s/, 'XXms'));
|
const lines = text.split('\n').filter(l => l.startsWith('0 :') || l.startsWith('1 :')).map(l => l.replace(/\d+(\.\d+)?m?s/, 'XXms'));
|
||||||
|
|
||||||
|
|
@ -185,10 +185,10 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
test.skip('skipped very long name', async () => {
|
test.skip('skipped very long name', async () => {
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { reporter: 'list', retries: 0 }, { PWTEST_TTY_WIDTH: '50' });
|
}, { reporter: 'list', retries: 0 }, { PLAYWRIGHT_FORCE_TTY: '50' });
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
|
|
||||||
const lines = result.output.split('\n').slice(3, 11);
|
const lines = result.rawOutput.split('\n').map(line => line.split('\x1B[22m\x1B[1E')).flat().map(line => stripAnsi(line)).filter(line => line.trim()).slice(1, 9);
|
||||||
expect(lines.every(line => line.length <= 50)).toBe(true);
|
expect(lines.every(line => line.length <= 50)).toBe(true);
|
||||||
|
|
||||||
expect(lines[0]).toBe(` 1 …a.test.ts:3:15 › failure in very long name`);
|
expect(lines[0]).toBe(` 1 …a.test.ts:3:15 › failure in very long name`);
|
||||||
|
|
|
||||||
|
|
@ -514,3 +514,47 @@ test('should report up to 3 timeout errors', async ({ runInlineTest }) => {
|
||||||
expect(result.output).toContain('Test timeout of 1000ms exceeded while running "afterEach" hook.');
|
expect(result.output).toContain('Test timeout of 1000ms exceeded while running "afterEach" hook.');
|
||||||
expect(result.output).toContain('Worker teardown timeout of 1000ms exceeded while tearing down "autoWorker".');
|
expect(result.output).toContain('Worker teardown timeout of 1000ms exceeded while tearing down "autoWorker".');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should complain when worker fixture times out during worker cleanup', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test as base, expect } from '@playwright/test';
|
||||||
|
const test = base.extend({
|
||||||
|
slowTeardown: [async ({}, use) => {
|
||||||
|
await use('hey');
|
||||||
|
await new Promise(f => setTimeout(f, 2000));
|
||||||
|
}, { scope: 'worker', auto: true, timeout: 400 }],
|
||||||
|
});
|
||||||
|
test('test ok', async ({ slowTeardown }) => {
|
||||||
|
expect(slowTeardown).toBe('hey');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
});
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.output).toContain(`Fixture "slowTeardown" timeout of 400ms exceeded during teardown.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should allow custom worker fixture timeout longer than force exit cap', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test as base, expect } from '@playwright/test';
|
||||||
|
const test = base.extend({
|
||||||
|
slowTeardown: [async ({}, use) => {
|
||||||
|
await use('hey');
|
||||||
|
await new Promise(f => setTimeout(f, 1500));
|
||||||
|
console.log('output from teardown');
|
||||||
|
throw new Error('Oh my!');
|
||||||
|
}, { scope: 'worker', auto: true, timeout: 2000 }],
|
||||||
|
});
|
||||||
|
test('test ok', async ({ slowTeardown }) => {
|
||||||
|
expect(slowTeardown).toBe('hey');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, {}, { PWTEST_FORCE_EXIT_TIMEOUT: '400' });
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.output).toContain(`output from teardown`);
|
||||||
|
expect(result.output).toContain(`Error: Oh my!`);
|
||||||
|
expect(result.output).toContain(`1 error was not a part of any test, see above for details`);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -285,3 +285,24 @@ test('should reveal errors in the sourcetab', async ({ runUITest }) => {
|
||||||
await page.getByText('a.spec.ts:4', { exact: true }).click();
|
await page.getByText('a.spec.ts:4', { exact: true }).click();
|
||||||
await expect(page.locator('.source-line-running')).toContainText(`throw new Error('Oh my');`);
|
await expect(page.locator('.source-line-running')).toContainText(`throw new Error('Oh my');`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should show request source context id', async ({ runUITest, server }) => {
|
||||||
|
const { page } = await runUITest({
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('pass', async ({ page, context, request }) => {
|
||||||
|
await page.goto('${server.EMPTY_PAGE}');
|
||||||
|
const page2 = await context.newPage();
|
||||||
|
await page2.goto('${server.EMPTY_PAGE}');
|
||||||
|
await request.get('${server.EMPTY_PAGE}');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByText('pass').dblclick();
|
||||||
|
await page.getByText('Network', { exact: true }).click();
|
||||||
|
await expect(page.locator('span').filter({ hasText: 'Source' })).toBeVisible();
|
||||||
|
await expect(page.getByText('page#1')).toBeVisible();
|
||||||
|
await expect(page.getByText('page#2')).toBeVisible();
|
||||||
|
await expect(page.getByText('api#1')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,10 @@ class ApiParser {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const clazz = new docs.Class(extractMetainfo(node), name, [], extendsName, extractComments(node));
|
const metainfo = extractMetainfo(node);
|
||||||
|
const clazz = new docs.Class(metainfo, name, [], extendsName, extractComments(node));
|
||||||
|
if (metainfo.hidden)
|
||||||
|
return;
|
||||||
this.classes.set(clazz.name, clazz);
|
this.classes.set(clazz.name, clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,13 +106,14 @@ class ApiParser {
|
||||||
returnType = new docs.Type('void');
|
returnType = new docs.Type('void');
|
||||||
|
|
||||||
const comments = extractComments(spec);
|
const comments = extractComments(spec);
|
||||||
|
const metainfo = extractMetainfo(spec);
|
||||||
let member;
|
let member;
|
||||||
if (match[1] === 'event')
|
if (match[1] === 'event')
|
||||||
member = docs.Member.createEvent(extractMetainfo(spec), name, returnType, comments);
|
member = docs.Member.createEvent(metainfo, name, returnType, comments);
|
||||||
if (match[1] === 'property')
|
if (match[1] === 'property')
|
||||||
member = docs.Member.createProperty(extractMetainfo(spec), name, returnType, comments, !optional);
|
member = docs.Member.createProperty(metainfo, name, returnType, comments, !optional);
|
||||||
if (['method', 'async method', 'optional method', 'optional async method'].includes(match[1])) {
|
if (['method', 'async method', 'optional method', 'optional async method'].includes(match[1])) {
|
||||||
member = docs.Member.createMethod(extractMetainfo(spec), name, [], returnType, comments);
|
member = docs.Member.createMethod(metainfo, name, [], returnType, comments);
|
||||||
if (match[1].includes('async'))
|
if (match[1].includes('async'))
|
||||||
member.async = true;
|
member.async = true;
|
||||||
if (match[1].includes('optional'))
|
if (match[1].includes('optional'))
|
||||||
|
|
@ -119,6 +123,11 @@ class ApiParser {
|
||||||
throw new Error('Unknown member: ' + spec.text);
|
throw new Error('Unknown member: ' + spec.text);
|
||||||
|
|
||||||
const clazz = /** @type {docs.Class} */(this.classes.get(match[2]));
|
const clazz = /** @type {docs.Class} */(this.classes.get(match[2]));
|
||||||
|
if (!clazz)
|
||||||
|
throw new Error(`Unknown class ${match[2]} for member: ` + spec.text);
|
||||||
|
if (metainfo.hidden)
|
||||||
|
return;
|
||||||
|
|
||||||
const existingMember = clazz.membersArray.find(m => m.name === name && m.kind === member.kind);
|
const existingMember = clazz.membersArray.find(m => m.name === name && m.kind === member.kind);
|
||||||
if (existingMember && isTypeOverride(existingMember, member)) {
|
if (existingMember && isTypeOverride(existingMember, member)) {
|
||||||
for (const lang of member?.langs?.only || []) {
|
for (const lang of member?.langs?.only || []) {
|
||||||
|
|
@ -157,6 +166,8 @@ class ApiParser {
|
||||||
throw new Error('Invalid member name ' + spec.text);
|
throw new Error('Invalid member name ' + spec.text);
|
||||||
if (match[1] === 'param') {
|
if (match[1] === 'param') {
|
||||||
const arg = this.parseProperty(spec);
|
const arg = this.parseProperty(spec);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
arg.name = name;
|
arg.name = name;
|
||||||
const existingArg = method.argsArray.find(m => m.name === arg.name);
|
const existingArg = method.argsArray.find(m => m.name === arg.name);
|
||||||
if (existingArg && isTypeOverride(existingArg, arg)) {
|
if (existingArg && isTypeOverride(existingArg, arg)) {
|
||||||
|
|
@ -171,13 +182,15 @@ class ApiParser {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// match[1] === 'option'
|
// match[1] === 'option'
|
||||||
|
const p = this.parseProperty(spec);
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
let options = method.argsArray.find(o => o.name === 'options');
|
let options = method.argsArray.find(o => o.name === 'options');
|
||||||
if (!options) {
|
if (!options) {
|
||||||
const type = new docs.Type('Object', []);
|
const type = new docs.Type('Object', []);
|
||||||
options = docs.Member.createProperty({ langs: {}, experimental: false, since: 'v1.0', deprecated: undefined, discouraged: undefined }, 'options', type, undefined, false);
|
options = docs.Member.createProperty({ langs: {}, since: 'v1.0', deprecated: undefined, discouraged: undefined }, 'options', type, undefined, false);
|
||||||
method.argsArray.push(options);
|
method.argsArray.push(options);
|
||||||
}
|
}
|
||||||
const p = this.parseProperty(spec);
|
|
||||||
p.required = false;
|
p.required = false;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options.type.properties.push(p);
|
options.type.properties.push(p);
|
||||||
|
|
@ -186,6 +199,7 @@ class ApiParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MarkdownHeaderNode} spec
|
* @param {MarkdownHeaderNode} spec
|
||||||
|
* @returns {docs.Member | null}
|
||||||
*/
|
*/
|
||||||
parseProperty(spec) {
|
parseProperty(spec) {
|
||||||
const param = childrenWithoutProperties(spec)[0];
|
const param = childrenWithoutProperties(spec)[0];
|
||||||
|
|
@ -196,12 +210,15 @@ class ApiParser {
|
||||||
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
||||||
const comments = extractComments(spec);
|
const comments = extractComments(spec);
|
||||||
const { type, optional } = this.parseType(/** @type {MarkdownLiNode} */(param));
|
const { type, optional } = this.parseType(/** @type {MarkdownLiNode} */(param));
|
||||||
return docs.Member.createProperty(extractMetainfo(spec), name, type, comments, !optional);
|
const metainfo = extractMetainfo(spec);
|
||||||
|
if (metainfo.hidden)
|
||||||
|
return null;
|
||||||
|
return docs.Member.createProperty(metainfo, name, type, comments, !optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MarkdownLiNode} spec
|
* @param {MarkdownLiNode} spec
|
||||||
* @return {{ type: docs.Type, optional: boolean, experimental: boolean }}
|
* @return {{ type: docs.Type, optional: boolean }}
|
||||||
*/
|
*/
|
||||||
parseType(spec) {
|
parseType(spec) {
|
||||||
const arg = parseVariable(spec.text);
|
const arg = parseVariable(spec.text);
|
||||||
|
|
@ -210,16 +227,16 @@ class ApiParser {
|
||||||
const { name, text } = parseVariable(/** @type {string} */(child.text));
|
const { name, text } = parseVariable(/** @type {string} */(child.text));
|
||||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||||
const childType = this.parseType(child);
|
const childType = this.parseType(child);
|
||||||
properties.push(docs.Member.createProperty({ langs: {}, experimental: childType.experimental, since: 'v1.0', deprecated: undefined, discouraged: undefined }, name, childType.type, comments, !childType.optional));
|
properties.push(docs.Member.createProperty({ langs: {}, since: 'v1.0', deprecated: undefined, discouraged: undefined }, name, childType.type, comments, !childType.optional));
|
||||||
}
|
}
|
||||||
const type = docs.Type.parse(arg.type, properties);
|
const type = docs.Type.parse(arg.type, properties);
|
||||||
return { type, optional: arg.optional, experimental: arg.experimental };
|
return { type, optional: arg.optional };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} line
|
* @param {string} line
|
||||||
* @returns {{ name: string, type: string, text: string, optional: boolean, experimental: boolean }}
|
* @returns {{ name: string, type: string, text: string, optional: boolean }}
|
||||||
*/
|
*/
|
||||||
function parseVariable(line) {
|
function parseVariable(line) {
|
||||||
let match = line.match(/^`([^`]+)` (.*)/);
|
let match = line.match(/^`([^`]+)` (.*)/);
|
||||||
|
|
@ -234,12 +251,9 @@ function parseVariable(line) {
|
||||||
const name = match[1];
|
const name = match[1];
|
||||||
let remainder = match[2];
|
let remainder = match[2];
|
||||||
let optional = false;
|
let optional = false;
|
||||||
let experimental = false;
|
while ('?'.includes(remainder[0])) {
|
||||||
while ('?e'.includes(remainder[0])) {
|
|
||||||
if (remainder[0] === '?')
|
if (remainder[0] === '?')
|
||||||
optional = true;
|
optional = true;
|
||||||
else if (remainder[0] === 'e')
|
|
||||||
experimental = true;
|
|
||||||
remainder = remainder.substring(1);
|
remainder = remainder.substring(1);
|
||||||
}
|
}
|
||||||
if (!remainder.startsWith('<'))
|
if (!remainder.startsWith('<'))
|
||||||
|
|
@ -252,7 +266,7 @@ function parseVariable(line) {
|
||||||
if (c === '>')
|
if (c === '>')
|
||||||
--depth;
|
--depth;
|
||||||
if (depth === 0)
|
if (depth === 0)
|
||||||
return { name, type: remainder.substring(1, i), text: remainder.substring(i + 2), optional, experimental };
|
return { name, type: remainder.substring(1, i), text: remainder.substring(i + 2), optional };
|
||||||
}
|
}
|
||||||
throw new Error('Should not be reached, line: ' + line);
|
throw new Error('Should not be reached, line: ' + line);
|
||||||
}
|
}
|
||||||
|
|
@ -344,15 +358,15 @@ function parseApi(apiDir, paramsPath) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MarkdownHeaderNode} spec
|
* @param {MarkdownHeaderNode} spec
|
||||||
* @returns {import('./documentation').Metainfo}
|
* @returns {import('./documentation').Metainfo & { hidden: boolean }}
|
||||||
*/
|
*/
|
||||||
function extractMetainfo(spec) {
|
function extractMetainfo(spec) {
|
||||||
return {
|
return {
|
||||||
langs: extractLangs(spec),
|
langs: extractLangs(spec),
|
||||||
since: extractSince(spec),
|
since: extractSince(spec),
|
||||||
experimental: extractExperimental(spec),
|
|
||||||
deprecated: extractAttribute(spec, 'deprecated'),
|
deprecated: extractAttribute(spec, 'deprecated'),
|
||||||
discouraged: extractAttribute(spec, 'discouraged'),
|
discouraged: extractAttribute(spec, 'discouraged'),
|
||||||
|
hidden: extractHidden(spec),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -402,9 +416,9 @@ function extractSince(spec) {
|
||||||
* @param {MarkdownHeaderNode} spec
|
* @param {MarkdownHeaderNode} spec
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function extractExperimental(spec) {
|
function extractHidden(spec) {
|
||||||
for (const child of spec.children) {
|
for (const child of spec.children) {
|
||||||
if (child.type === 'li' && child.liType === 'bullet' && child.text === 'experimental')
|
if (child.type === 'li' && child.liType === 'bullet' && child.text === 'hidden')
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -429,7 +443,7 @@ function extractSince(spec) {
|
||||||
*/
|
*/
|
||||||
function childrenWithoutProperties(spec) {
|
function childrenWithoutProperties(spec) {
|
||||||
return (spec.children || []).filter(c => {
|
return (spec.children || []).filter(c => {
|
||||||
const isProperty = c.type === 'li' && c.liType === 'bullet' && (c.text.startsWith('langs:') || c.text.startsWith('since:') || c.text.startsWith('deprecated:') || c.text.startsWith('discouraged:') || c.text === 'experimental');
|
const isProperty = c.type === 'li' && c.liType === 'bullet' && (c.text.startsWith('langs:') || c.text.startsWith('since:') || c.text.startsWith('deprecated:') || c.text.startsWith('discouraged:') || c.text === 'hidden');
|
||||||
return !isProperty;
|
return !isProperty;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ const md = require('../markdown');
|
||||||
* since: string,
|
* since: string,
|
||||||
* deprecated?: string | undefined,
|
* deprecated?: string | undefined,
|
||||||
* discouraged?: string | undefined,
|
* discouraged?: string | undefined,
|
||||||
* experimental: boolean
|
|
||||||
* }} Metainfo
|
* }} Metainfo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -132,18 +131,6 @@ class Documentation {
|
||||||
this.index();
|
this.index();
|
||||||
}
|
}
|
||||||
|
|
||||||
filterOutExperimental() {
|
|
||||||
const classesArray = [];
|
|
||||||
for (const clazz of this.classesArray) {
|
|
||||||
if (clazz.experimental)
|
|
||||||
continue;
|
|
||||||
clazz.filterOutExperimental();
|
|
||||||
classesArray.push(clazz);
|
|
||||||
}
|
|
||||||
this.classesArray = classesArray;
|
|
||||||
this.index();
|
|
||||||
}
|
|
||||||
|
|
||||||
index() {
|
index() {
|
||||||
for (const cls of this.classesArray) {
|
for (const cls of this.classesArray) {
|
||||||
this.classes.set(cls.name, cls);
|
this.classes.set(cls.name, cls);
|
||||||
|
|
@ -231,7 +218,6 @@ class Documentation {
|
||||||
*/
|
*/
|
||||||
constructor(metainfo, name, membersArray, extendsName = null, spec = undefined) {
|
constructor(metainfo, name, membersArray, extendsName = null, spec = undefined) {
|
||||||
this.langs = metainfo.langs;
|
this.langs = metainfo.langs;
|
||||||
this.experimental = metainfo.experimental;
|
|
||||||
this.since = metainfo.since;
|
this.since = metainfo.since;
|
||||||
this.deprecated = metainfo.deprecated;
|
this.deprecated = metainfo.deprecated;
|
||||||
this.discouraged = metainfo.discouraged;
|
this.discouraged = metainfo.discouraged;
|
||||||
|
|
@ -286,7 +272,7 @@ class Documentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
const cls = new Class({ langs: this.langs, experimental: this.experimental, since: this.since, deprecated: this.deprecated, discouraged: this.discouraged }, this.name, this.membersArray.map(m => m.clone()), this.extends, this.spec);
|
const cls = new Class({ langs: this.langs, since: this.since, deprecated: this.deprecated, discouraged: this.discouraged }, this.name, this.membersArray.map(m => m.clone()), this.extends, this.spec);
|
||||||
cls.comment = this.comment;
|
cls.comment = this.comment;
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
@ -306,17 +292,6 @@ class Documentation {
|
||||||
this.membersArray = membersArray;
|
this.membersArray = membersArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
filterOutExperimental() {
|
|
||||||
const membersArray = [];
|
|
||||||
for (const member of this.membersArray) {
|
|
||||||
if (member.experimental)
|
|
||||||
continue;
|
|
||||||
member.filterOutExperimental();
|
|
||||||
membersArray.push(member);
|
|
||||||
}
|
|
||||||
this.membersArray = membersArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortMembers() {
|
sortMembers() {
|
||||||
/**
|
/**
|
||||||
* @param {Member} member
|
* @param {Member} member
|
||||||
|
|
@ -362,7 +337,6 @@ class Member {
|
||||||
constructor(kind, metainfo, name, type, argsArray, spec = undefined, required = true) {
|
constructor(kind, metainfo, name, type, argsArray, spec = undefined, required = true) {
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.langs = metainfo.langs;
|
this.langs = metainfo.langs;
|
||||||
this.experimental = metainfo.experimental;
|
|
||||||
this.since = metainfo.since;
|
this.since = metainfo.since;
|
||||||
this.deprecated = metainfo.deprecated;
|
this.deprecated = metainfo.deprecated;
|
||||||
this.discouraged = metainfo.discouraged;
|
this.discouraged = metainfo.discouraged;
|
||||||
|
|
@ -447,22 +421,8 @@ class Member {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filterOutExperimental() {
|
|
||||||
if (!this.type)
|
|
||||||
return;
|
|
||||||
this.type.filterOutExperimental();
|
|
||||||
const argsArray = [];
|
|
||||||
for (const arg of this.argsArray) {
|
|
||||||
if (arg.experimental || !arg.type)
|
|
||||||
continue;
|
|
||||||
arg.type.filterOutExperimental();
|
|
||||||
argsArray.push(arg);
|
|
||||||
}
|
|
||||||
this.argsArray = argsArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
const result = new Member(this.kind, { langs: this.langs, experimental: this.experimental, since: this.since, deprecated: this.deprecated, discouraged: this.discouraged }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
const result = new Member(this.kind, { langs: this.langs, since: this.since, deprecated: this.deprecated, discouraged: this.discouraged }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||||
result.alias = this.alias;
|
result.alias = this.alias;
|
||||||
result.async = this.async;
|
result.async = this.async;
|
||||||
result.paramOrOption = this.paramOrOption;
|
result.paramOrOption = this.paramOrOption;
|
||||||
|
|
@ -671,19 +631,6 @@ class Type {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
filterOutExperimental() {
|
|
||||||
if (!this.properties)
|
|
||||||
return;
|
|
||||||
const properties = [];
|
|
||||||
for (const prop of this.properties) {
|
|
||||||
if (prop.experimental)
|
|
||||||
continue;
|
|
||||||
prop.filterOutExperimental();
|
|
||||||
properties.push(prop);
|
|
||||||
}
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Type[]} result
|
* @param {Type[]} result
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ class TypesGenerator {
|
||||||
* ignoreMissing?: Set<string>,
|
* ignoreMissing?: Set<string>,
|
||||||
* doNotExportClassNames?: Set<string>,
|
* doNotExportClassNames?: Set<string>,
|
||||||
* doNotGenerate?: Set<string>,
|
* doNotGenerate?: Set<string>,
|
||||||
* includeExperimental?: boolean,
|
|
||||||
* }} options
|
* }} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
|
@ -50,8 +49,6 @@ class TypesGenerator {
|
||||||
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
|
this.doNotExportClassNames = options.doNotExportClassNames || new Set();
|
||||||
this.doNotGenerate = options.doNotGenerate || new Set();
|
this.doNotGenerate = options.doNotGenerate || new Set();
|
||||||
this.documentation.filterForLanguage('js');
|
this.documentation.filterForLanguage('js');
|
||||||
if (!options.includeExperimental)
|
|
||||||
this.documentation.filterOutExperimental();
|
|
||||||
this.documentation.copyDocsFromSuperclasses([]);
|
this.documentation.copyDocsFromSuperclasses([]);
|
||||||
this.injectDisposeAsync();
|
this.injectDisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +62,7 @@ class TypesGenerator {
|
||||||
continue;
|
continue;
|
||||||
if (!member.async)
|
if (!member.async)
|
||||||
continue;
|
continue;
|
||||||
newMember = new docs.Member('method', { langs: {}, since: '1.0', experimental: false }, '[Symbol.asyncDispose]', null, []);
|
newMember = new docs.Member('method', { langs: {}, since: '1.0' }, '[Symbol.asyncDispose]', null, []);
|
||||||
newMember.async = true;
|
newMember.async = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -98,12 +95,20 @@ class TypesGenerator {
|
||||||
}, (className, methodName, overloadIndex) => {
|
}, (className, methodName, overloadIndex) => {
|
||||||
if (className === 'SuiteFunction' && methodName === '__call') {
|
if (className === 'SuiteFunction' && methodName === '__call') {
|
||||||
const cls = this.documentation.classes.get('Test');
|
const cls = this.documentation.classes.get('Test');
|
||||||
|
if (!cls)
|
||||||
|
throw new Error(`Unknown class "Test"`);
|
||||||
const method = cls.membersArray.find(m => m.alias === 'describe');
|
const method = cls.membersArray.find(m => m.alias === 'describe');
|
||||||
|
if (!method)
|
||||||
|
throw new Error(`Unknown method "Test.describe"`);
|
||||||
return this.memberJSDOC(method, ' ').trimLeft();
|
return this.memberJSDOC(method, ' ').trimLeft();
|
||||||
}
|
}
|
||||||
if (className === 'TestFunction' && methodName === '__call') {
|
if (className === 'TestFunction' && methodName === '__call') {
|
||||||
const cls = this.documentation.classes.get('Test');
|
const cls = this.documentation.classes.get('Test');
|
||||||
|
if (!cls)
|
||||||
|
throw new Error(`Unknown class "Test"`);
|
||||||
const method = cls.membersArray.find(m => m.alias === '(call)');
|
const method = cls.membersArray.find(m => m.alias === '(call)');
|
||||||
|
if (!method)
|
||||||
|
throw new Error(`Unknown method "Test.(call)"`);
|
||||||
return this.memberJSDOC(method, ' ').trimLeft();
|
return this.memberJSDOC(method, ' ').trimLeft();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +142,8 @@ class TypesGenerator {
|
||||||
.filter(cls => !handledClasses.has(cls.name));
|
.filter(cls => !handledClasses.has(cls.name));
|
||||||
{
|
{
|
||||||
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
|
const playwright = this.documentation.classesArray.find(c => c.name === 'Playwright');
|
||||||
|
if (!playwright)
|
||||||
|
throw new Error(`Unknown class "Playwright"`);
|
||||||
playwright.membersArray = playwright.membersArray.filter(member => !['errors', 'devices'].includes(member.name));
|
playwright.membersArray = playwright.membersArray.filter(member => !['errors', 'devices'].includes(member.name));
|
||||||
playwright.index();
|
playwright.index();
|
||||||
}
|
}
|
||||||
|
|
@ -327,19 +334,20 @@ class TypesGenerator {
|
||||||
hasOwnMethod(classDesc, member) {
|
hasOwnMethod(classDesc, member) {
|
||||||
if (this.handledMethods.has(`${classDesc.name}.${member.alias}#${member.overloadIndex}`))
|
if (this.handledMethods.has(`${classDesc.name}.${member.alias}#${member.overloadIndex}`))
|
||||||
return false;
|
return false;
|
||||||
while (classDesc = this.parentClass(classDesc)) {
|
let parent = /** @type {docs.Class | undefined} */ (classDesc);
|
||||||
if (classDesc.members.has(member.alias))
|
while (parent = this.parentClass(parent)) {
|
||||||
|
if (parent.members.has(member.alias))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {docs.Class} classDesc
|
* @param {docs.Class | undefined} classDesc
|
||||||
*/
|
*/
|
||||||
parentClass(classDesc) {
|
parentClass(classDesc) {
|
||||||
if (!classDesc.extends)
|
if (!classDesc || !classDesc.extends)
|
||||||
return null;
|
return;
|
||||||
return this.documentation.classes.get(classDesc.extends);
|
return this.documentation.classes.get(classDesc.extends);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,6 +435,8 @@ class TypesGenerator {
|
||||||
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
||||||
const shouldExport = exported[name];
|
const shouldExport = exported[name];
|
||||||
const properties = namespace[namespace.length - 1] === 'options' ? type.sortedProperties() : type.properties;
|
const properties = namespace[namespace.length - 1] === 'options' ? type.sortedProperties() : type.properties;
|
||||||
|
if (!properties)
|
||||||
|
throw new Error(`Object type must have properties`);
|
||||||
if (!this.objectDefinitions.some(o => o.name === name))
|
if (!this.objectDefinitions.some(o => o.name === name))
|
||||||
this.objectDefinitions.push({ name, properties });
|
this.objectDefinitions.push({ name, properties });
|
||||||
if (shouldExport) {
|
if (shouldExport) {
|
||||||
|
|
@ -503,15 +513,13 @@ class TypesGenerator {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} includeExperimental
|
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async function generateCoreTypes(includeExperimental) {
|
async function generateCoreTypes() {
|
||||||
const documentation = coreDocumentation.clone();
|
const documentation = coreDocumentation.clone();
|
||||||
const generator = new TypesGenerator({
|
const generator = new TypesGenerator({
|
||||||
documentation,
|
documentation,
|
||||||
doNotGenerate: assertionClasses,
|
doNotGenerate: assertionClasses,
|
||||||
includeExperimental,
|
|
||||||
});
|
});
|
||||||
let types = await generator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
|
let types = await generator.generateTypes(path.join(__dirname, 'overrides.d.ts'));
|
||||||
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
|
const namedDevices = Object.keys(devices).map(name => ` ${JSON.stringify(name)}: DeviceDescriptor;`).join('\n');
|
||||||
|
|
@ -534,10 +542,9 @@ class TypesGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} includeExperimental
|
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async function generateTestTypes(includeExperimental) {
|
async function generateTestTypes() {
|
||||||
const documentation = coreDocumentation.mergeWith(testDocumentation);
|
const documentation = coreDocumentation.mergeWith(testDocumentation);
|
||||||
const generator = new TypesGenerator({
|
const generator = new TypesGenerator({
|
||||||
documentation,
|
documentation,
|
||||||
|
|
@ -574,16 +581,14 @@ class TypesGenerator {
|
||||||
'TestFunction',
|
'TestFunction',
|
||||||
]),
|
]),
|
||||||
doNotExportClassNames: assertionClasses,
|
doNotExportClassNames: assertionClasses,
|
||||||
includeExperimental,
|
|
||||||
});
|
});
|
||||||
return await generator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
|
return await generator.generateTypes(path.join(__dirname, 'overrides-test.d.ts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} includeExperimental
|
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async function generateReporterTypes(includeExperimental) {
|
async function generateReporterTypes() {
|
||||||
const documentation = coreDocumentation.mergeWith(testDocumentation).mergeWith(reporterDocumentation);
|
const documentation = coreDocumentation.mergeWith(testDocumentation).mergeWith(reporterDocumentation);
|
||||||
const generator = new TypesGenerator({
|
const generator = new TypesGenerator({
|
||||||
documentation,
|
documentation,
|
||||||
|
|
@ -601,7 +606,6 @@ class TypesGenerator {
|
||||||
'JSONReportTestResult',
|
'JSONReportTestResult',
|
||||||
'JSONReportTestStep',
|
'JSONReportTestStep',
|
||||||
]),
|
]),
|
||||||
includeExperimental,
|
|
||||||
});
|
});
|
||||||
return await generator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'));
|
return await generator.generateTypes(path.join(__dirname, 'overrides-testReporter.d.ts'));
|
||||||
}
|
}
|
||||||
|
|
@ -629,9 +633,9 @@ class TypesGenerator {
|
||||||
if (!fs.existsSync(playwrightTypesDir))
|
if (!fs.existsSync(playwrightTypesDir))
|
||||||
fs.mkdirSync(playwrightTypesDir)
|
fs.mkdirSync(playwrightTypesDir)
|
||||||
writeFile(path.join(coreTypesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'), false);
|
writeFile(path.join(coreTypesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'packages', 'playwright-core', 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'), false);
|
||||||
writeFile(path.join(coreTypesDir, 'types.d.ts'), await generateCoreTypes(false), true);
|
writeFile(path.join(coreTypesDir, 'types.d.ts'), await generateCoreTypes(), true);
|
||||||
writeFile(path.join(playwrightTypesDir, 'test.d.ts'), await generateTestTypes(false), true);
|
writeFile(path.join(playwrightTypesDir, 'test.d.ts'), await generateTestTypes(), true);
|
||||||
writeFile(path.join(playwrightTypesDir, 'testReporter.d.ts'), await generateReporterTypes(false), true);
|
writeFile(path.join(playwrightTypesDir, 'testReporter.d.ts'), await generateReporterTypes(), true);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
})().catch(e => {
|
})().catch(e => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,6 @@ Example:
|
||||||
'wk': 'webkit',
|
'wk': 'webkit',
|
||||||
}[args[0].toLowerCase()] ?? args[0].toLowerCase();
|
}[args[0].toLowerCase()] ?? args[0].toLowerCase();
|
||||||
const descriptors = [browsersJSON.browsers.find(b => b.name === browserName)];
|
const descriptors = [browsersJSON.browsers.find(b => b.name === browserName)];
|
||||||
if (browserName === 'firefox')
|
|
||||||
descriptors.push(browsersJSON.browsers.find(b => b.name === 'firefox-asan'));
|
|
||||||
|
|
||||||
if (!descriptors.every(d => !!d)) {
|
if (!descriptors.every(d => !!d)) {
|
||||||
console.log(`Unknown browser "${browserName}"`);
|
console.log(`Unknown browser "${browserName}"`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue